SCOI2013 day2 数数(count)
好吧,这是我两天里唯一考试时AC的题目…………
明显的按位DP,设sum1[i][lim][zero]表示在第i位,是否顶着上界,是否有前导零的所有数的前缀串之和(如12345的前缀串之和为12345+1234+123+12+1),sum2[i][lim][zero]记录的是所求的和,num[i][lim][zero]表示该状态下数的个数。那么转移很明显了:sum1[i]=now*tms[i]*num[i-1]+sum1[i-1],sum2[i]=sum2[i-1]+now*tms[i]*num[i-1]+sum1[i-1]*!(zero&&(now==0));(now为当前这一位,tms[i]=b^i+b^(i-1)+...+b^0,lim和zero略去,因为和普通按位DP一样)
当然,写出方程以后能得50分,剩下的50分因为进制数太大,需要优化转移。可以发现状态记录中不包括当前位是什么,也就是说不必枚举当前位,只需分情况讨论后(如上界,当前位为0),每种情况处理一次,然后用等差数列求和之类的东西推出该情况的所有部分就行了。
ps 记忆化要爆栈,需要想办法解决。
总之就一道水题…………没什么思维难度的……………我这种弱菜也就只做的起这种题了………………
代码,求大神轻虐!!(注释掉的那一段是没有优化转移的):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 #define inc(a,b) a+=b,a=a>=MOD?a-MOD:a 7 const int MOD=20130427; 8 const int MAXN=100010; 9 10 struct Ans 11 { 12 int num,sum1,sum2; 13 Ans() {sum1=sum2=num=0;} 14 Ans(int num,int sum1,int sum2):num(num),sum1(sum1),sum2(sum2) {} 15 }dp[MAXN][2][2]; 16 17 bool dped[MAXN][2][2]; 18 int tms[MAXN],num[MAXN],n,ans1,ans2,B; 19 20 //srO __TY Orz 21 Ans fdp(int now,int flag,int lim) 22 { 23 if (now<0 && flag) return Ans(0,0,0); 24 if (now<0) return Ans(1,0,0); 25 if (dped[now][flag][lim]) 26 return dp[now][flag][lim]; 27 dped[now][flag][lim]=1; 28 int up=lim?num[now]:B-1; 29 Ans &ret=dp[now][flag][lim],cur; 30 dp[now][flag][lim]=Ans(); 31 /*for (int i=0;i<=up;++i) 32 { 33 cur=fdp(now-1,flag&&(i==0),lim&&i==up); 34 inc(ret.num,cur.num); 35 ret.sum1=((long long)ret.sum1+cur.sum1+(long long)i*cur.num*tms[now]%MOD)%MOD; 36 ret.sum2=((long long)ret.sum2+cur.sum2+(long long)i*cur.num*tms[now]%MOD+(!(flag&&(i==0)))*cur.sum1)%MOD; 37 }*/ 38 cur=fdp(now-1,flag,lim&&(up==0)); 39 inc(ret.num,cur.num); 40 inc(ret.sum1,cur.sum1); 41 inc(ret.sum2,cur.sum2); 42 if (!flag) inc(ret.sum2,cur.sum1); 43 if (up>0) 44 { 45 cur=fdp(now-1,0,lim); 46 inc(ret.num,cur.num); 47 ret.sum1=((long long)ret.sum1+cur.sum1+(long long)up*cur.num%MOD*tms[now]%MOD)%MOD; 48 ret.sum2=((long long)ret.sum2+cur.sum2+(long long)up*cur.num%MOD*tms[now]%MOD+cur.sum1)%MOD; 49 if (up>1) 50 { 51 cur=fdp(now-1,0,0); 52 ret.num=((long long)ret.num+(long long)cur.num*(up-1)%MOD)%MOD; 53 ret.sum1=((long long)ret.sum1+(long long)cur.sum1*(up-1)%MOD)%MOD; 54 ret.sum1=((long long)ret.sum1+((((long long)up*(up-1)>>1)%MOD*cur.num%MOD)*tms[now])%MOD)%MOD; 55 ret.sum2=((long long)ret.sum2+(long long)cur.sum2*(up-1)%MOD)%MOD; 56 ret.sum2=((long long)ret.sum2+((((long long)up*(up-1)>>1)%MOD*cur.num%MOD)*tms[now])%MOD)%MOD; 57 ret.sum2=((long long)ret.sum2+(long long)cur.sum1*(up-1)%MOD)%MOD; 58 } 59 } 60 return dp[now][flag][lim]; 61 } 62 63 //srO __TY Orz 64 int main() 65 { 66 freopen("count.in","r",stdin); 67 freopen("count.out","w",stdout); 68 tms[0]=1; 69 scanf("%d",&B); 70 for (int i=1;i<MAXN;++i) 71 tms[i]=((long long)tms[i-1]*B+1)%MOD; 72 scanf("%d",&n); 73 for (int i=n-1;i>=0;--i) 74 scanf("%d",&num[i]); 75 while (!num[n-1] && n>1) --n; 76 if (!(num[0]==0 && n==1)) 77 { 78 --num[0]; 79 int x=0; 80 while (num[x]<0) 81 { 82 num[x]+=B; 83 --num[x+1]; 84 ++x; 85 } 86 } 87 while (!num[n-1]) --n; 88 for (int i=0;i<n;++i) 89 { 90 fdp(i,1,1); 91 fdp(i,1,0); 92 fdp(i,0,1); 93 fdp(i,0,0); 94 } 95 ans1=fdp(n-1,1,1).sum2; 96 memset(dped,0,sizeof(dped)); 97 scanf("%d",&n); 98 for (int i=n-1;i>=0;--i) 99 scanf("%d",&num[i]); 100 for (int i=0;i<n;++i) 101 { 102 fdp(i,1,1); 103 fdp(i,1,0); 104 fdp(i,0,1); 105 fdp(i,0,0); 106 } 107 ans2=fdp(n-1,1,1).sum2; 108 printf("%d\n",ans2-ans1<0?ans2-ans1+MOD:ans2-ans1); 109 return 0; 110 }