Warning: session_start(): open(/tmp/sess_4f4bb91279a90f95152ef9e5ee5651e6, O_RDWR) failed: No space left on device (28) in /data/wiki/inc/init.php on line 239

Warning: session_start(): Failed to read session data: files (path: ) in /data/wiki/inc/init.php on line 239

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/auth.php on line 430

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/actions.php on line 38

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/lib/tpl/dokuwiki/main.php on line 12
2020-2021:teams:legal_string:jxm2001:动态规划_1 [CVBB ACM Team]

用户工具

站点工具


2020-2021:teams:legal_string:jxm2001:动态规划_1

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
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>​
2020-2021/teams/legal_string/jxm2001/动态规划_1.1596367523.txt.gz · 最后更改: 2020/08/02 19:25 由 jxm2001