两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
2020-2021:teams:legal_string:jxm2001:动态规划_1 [2020/08/02 19:25] jxm2001 |
2020-2021:teams:legal_string:jxm2001:动态规划_1 [2021/01/28 20:37] (当前版本) jxm2001 |
||
---|---|---|---|
行 26: | 行 26: | ||
== 题解 == | == 题解 == | ||
+ | |||
+ | 搜索函数中 $s$ 记录前一位状态即可。 | ||
<hidden 查看代码> | <hidden 查看代码> | ||
行 238: | 行 240: | ||
=== 习题五 === | === 习题五 === | ||
- | [[https://ac.nowcoder.com/acm/contest/5671/H|洛谷p3413]] | + | [[https://ac.nowcoder.com/acm/contest/5671/H|牛客暑期多校(第六场) H 题]] |
== 题意 == | == 题意 == | ||
- | 求 $1\le A\le B\le N$ ,满足 $S(A)\gt S(B)$ 的 $(A,B)$ 个数,其中 $S(n)$ 代表 $n$ 的数码和。 | + | 求 $0\le A\le B\le N$ ,满足 $S(A)\gt S(B)$ 的 $(A,B)$ 个数,其中 $S(n)$ 代表 $n$ 的数码和。 |
== 题解 == | == 题解 == | ||
+ | |||
+ | 搜索函数为 $f(pos,d,eq1,eq2)$。其中 $d$ 表示 $S(B)-S(A)$,$eq1$ 表示 $B$ 前缀是否与查询上界相等,$eq2$ 表示 $A$ 前缀是否与 $B$ 前缀相等。 | ||
+ | |||
+ | 接下来同时枚举 $B,A$ 在 $pos$ 位的数值即可,合法状态共 $O(18L^2)$ 个,每个状态递推时间复杂度 $O(100)$。 | ||
+ | |||
+ | 于是总时间复杂度 $O(1800L^2)$,其中 $L$ 表示 $N$ 的长度。需要注意搜索过程中需要对 $eq1,eq2$ 进行记忆化。 | ||
+ | |||
+ | 单个数搜索时不需要对 $eq$ 进行记忆化是因为单个数搜索时从 $eq=true$ 的状态只能走到下一个 $eq=true$ 的状态。 | ||
+ | |||
+ | 而两个数搜索时,从 $eq1=true,eq2=false$ 的状态可以走到 $10$ 个 $eq1=true,eq2=false$ 的状态。 | ||
+ | |||
+ | 所以存在某些 $eq1=true,eq2=false$ 的状态会被重复访问的可能,对 $eq1=false,eq2=true$ 的情况类似。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | const int MAXL=105,MAXV=905,Mod=1e9+7; | ||
+ | int a[MAXL],dp[MAXL][MAXV<<1][2][2]; | ||
+ | int dfs(int pos,int d,bool eq1,bool eq2){ | ||
+ | if(d>=MAXV+pos*9)return 0; | ||
+ | if(!pos)return 1; | ||
+ | if(~dp[pos][d][eq1][eq2]) | ||
+ | return dp[pos][d][eq1][eq2]; | ||
+ | int ans=0,v1=eq1?a[pos]:9,v2; | ||
+ | _rep(i,0,v1){ | ||
+ | v2=eq2?i:9; | ||
+ | _rep(j,0,v2) | ||
+ | ans=(ans+dfs(pos-1,d+i-j,eq1&&i==v1,eq2&&j==v2))%Mod; | ||
+ | } | ||
+ | return dp[pos][d][eq1][eq2]=ans; | ||
+ | } | ||
+ | int solve(char *s){ | ||
+ | int len=strlen(s); | ||
+ | mem(dp,-1); | ||
+ | _rep(i,1,len) | ||
+ | a[i]=s[len-i]-'0'; | ||
+ | return dfs(len,MAXV,true,true); | ||
+ | } | ||
+ | char buf[MAXL]; | ||
+ | int main() | ||
+ | { | ||
+ | scanf("%s",buf); | ||
+ | enter(solve(buf)); | ||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | === 习题六 === | ||
+ | |||
+ | [[https://cometoj.com/contest/71/problem/D?problem_id=4019|Comet OJ-Contest #12 D题]] | ||
+ | |||
+ | == 题意 == | ||
+ | |||
+ | 求满足 $0\le x\le A,0\le y\le B,x\oplus y=n,|x-y|\le m$ 的 $(x,y)$ 个数。 | ||
+ | |||
+ | == 题解 == | ||
+ | |||
+ | 不难想到按二进制 $\text{dp}$。但 $|x-y|$ 在 $\text{dp}$ 过程中可能上升,也可能下降,不方便处理。 | ||
+ | |||
+ | 故不妨假设初始时 $x=0,y=n$,然后若 $n$ 的第 $i$ 位等于 $1$ 且将 $1$ 交给 $x$,则 $x-y$ 增加 $2^{i+1}$。这样可以保证 $x-y$ 在 $\text{dp}$ 过程中单增。 | ||
+ | |||
+ | $|x-y|\le m$ 等价于增量 $n-m\le d\le n+m$,考虑计算出 $d\le n+m$ 的值减去 $d\le n-m-1$ 的值即可。 | ||
+ | |||
+ | 另外注意 $n-m\le 0$ 的情况需要特判防止 $\text{dp}$ 出错,由于 $|x-y|\le n$ 必成立,所以结果为 $0$。 | ||
+ | |||
+ | 搜索函数为 $f(pos,eq1,eq2,eq3)$。其中 $eq1$ 表示 $x$ 前缀是否与 $A$ 前缀相等,$eq2$ 表示 $y$ 前缀是否与 $B$ 前缀相等。 | ||
+ | |||
+ | $eq3$ 表示 $d$ 前缀是否与上界相等,然后枚举每一位可能,使其满足约束 $x\oplus y=n$,直接转移即可。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | const int MAXL=64; | ||
+ | int A[MAXL],B[MAXL],N[MAXL],M[MAXL]; | ||
+ | LL dp[MAXL][2][2][2]; | ||
+ | void cal(LL x,int *X){ | ||
+ | _for(i,1,MAXL) | ||
+ | X[i]=0; | ||
+ | int pos=0; | ||
+ | while(x){ | ||
+ | X[++pos]=x&1; | ||
+ | x>>=1; | ||
+ | } | ||
+ | } | ||
+ | LL dfs(int pos,bool eq1,bool eq2,bool eq3){ | ||
+ | if(!pos)return 1; | ||
+ | if(~dp[pos][eq1][eq2][eq3])return dp[pos][eq1][eq2][eq3]; | ||
+ | LL ans=0; | ||
+ | _for(i,0,2){ | ||
+ | int x=i,y=i^N[pos]; | ||
+ | if((eq1&&x>A[pos])||(eq2&&y>B[pos])||(eq3&&((x>y)>M[pos+1]))) | ||
+ | continue; | ||
+ | ans+=dfs(pos-1,eq1&&x==A[pos],eq2&&y==B[pos],eq3&&(x>y)==M[pos+1]); | ||
+ | } | ||
+ | return dp[pos][eq1][eq2][eq3]=ans; | ||
+ | } | ||
+ | LL solve(LL m){ | ||
+ | mem(dp,-1); | ||
+ | cal(m,M); | ||
+ | return dfs(62,true,true,true); | ||
+ | } | ||
+ | int main() | ||
+ | { | ||
+ | int T=read_int(); | ||
+ | while(T--){ | ||
+ | LL a=read_LL(),b=read_LL(),n=read_LL(),m=read_LL(); | ||
+ | cal(a,A); | ||
+ | cal(b,B); | ||
+ | cal(n,N); | ||
+ | enter(solve(n+m)-((n-m>0)?solve(n-m-1):0)); | ||
+ | } | ||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | === 习题七 === | ||
+ | |||
+ | [[https://www.luogu.com.cn/problem/CF908G|CF908G]] | ||
+ | |||
+ | == 题意 == | ||
+ | |||
+ | 定义 $S(x)$ 表示将 $x$ 的每个位从小到大重排后的值,例如 $S(3190)=0139,S(4312)=1234$。 | ||
+ | |||
+ | 给定 $1\le X\le 10^{700}$,求 | ||
+ | |||
+ | $$ | ||
+ | \sum_{i=1}^X S(i) | ||
+ | $$ | ||
+ | |||
+ | == 题解 == | ||
+ | |||
+ | 考虑按贡献计算,设 $f(i,j)$ 表示 $1\sim X$ 中满足 $S(x)$ 的第 $j$ 位等于 $i$ 的 $x$ 的个数,$n=\text{len}(X)$,于是 | ||
+ | |||
+ | $$ | ||
+ | ans=\sum_{i=1}^9i\sum_{j=1}^n f(i,j)10^{j-1}=\sum_{i=1}^{9}i\sum_{j=1}^n (g(i,j)-g(i+1,j))10^{j-1} | ||
+ | $$ | ||
+ | |||
+ | 其中 $g(i,j)=\sum_{k=i}^9f(i,k)$ 表示 $1\sim X$ 中满足 $S(x)$ 的第 $j$ 位大于等于 $i$ 的 $x$ 的个数。 | ||
+ | |||
+ | 不难发现 $g(i,j)$ 也等于 $1\sim X$ 中满足 $x$ 中至少有 $j$ 位大于等于 $i$ 的 $x$ 的个数,于是 | ||
+ | |||
+ | $$ | ||
+ | g(i,j)=\sum_{k=j}^n \text{dp}(i,k) | ||
+ | $$ | ||
+ | |||
+ | 其中 $\text{dp}(i,j)$ 表示 $1\sim X$ 中满足 $x$ 中有 $j$ 位大于等于 $i$ 的 $x$ 的个数,显然该值通过数位 $\text{DP}$ 即可计算。 | ||
+ | |||
+ | 时间复杂度 $O(100n^2)$。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | const int MAXL=705,Mod=1e9+7; | ||
+ | char s[MAXL]; | ||
+ | int a[MAXL],dp[10][MAXL][MAXL][2],suf[11][MAXL]; | ||
+ | int dfs(int v,int pos,int cnt,bool eq){ | ||
+ | if(!pos||cnt<0)return cnt==0; | ||
+ | if(~dp[v][pos][cnt][eq])return dp[v][pos][cnt][eq]; | ||
+ | int ans=0,mv=eq?a[pos]:9; | ||
+ | _rep(i,0,mv) | ||
+ | ans=(ans+dfs(v,pos-1,cnt-(i>=v),eq&&(a[pos]==i)))%Mod; | ||
+ | return dp[v][pos][cnt][eq]=ans; | ||
+ | } | ||
+ | int main() | ||
+ | { | ||
+ | mem(dp,-1); | ||
+ | scanf("%s",s+1); | ||
+ | int len=strlen(s+1); | ||
+ | for(int i=1,j=len;i<=len;i++,j--)a[i]=s[j]-'0'; | ||
+ | int ans=0; | ||
+ | _for(i,1,10)for(int j=len;j>=1;j--) | ||
+ | suf[i][j]=(suf[i][j+1]+dfs(i,len,j,true))%Mod; | ||
+ | _for(i,1,10){ | ||
+ | int p=1; | ||
+ | _rep(j,1,len){ | ||
+ | ans=(ans+1LL*i*p%Mod*(suf[i][j]-suf[i+1][j]+Mod)%Mod)%Mod; | ||
+ | p=10LL*p%Mod; | ||
+ | } | ||
+ | } | ||
+ | enter(ans); | ||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> |