[BZOJ 3326] 数数
Link:
Solution:
明显是一道数位$dp$的题目,就是递推式复杂了点
先要求出一个数$\bar{n}$向添加一位后的$\bar{np}$的转化关系
令$res[\bar{n}]$为数$n$的权值和,
则$res[\bar{np}]=res[\bar{n}]+\sum_{i=1}^{len(n)} \bar{n[i...len(n)]p}$
令$suf[\bar{n}]$为数$n$的后缀和,
则$res[\bar{np}]=res[\bar{n}]+suf[\bar{np}]$
同时$suf$自己的递推式为:$suf[\bar{np}]=base*suf[\bar{n}]+(len(n)+1)*p$
再令$dgt[\bar{n}]$为数$n$的位数,
则$dgt[\bar{np}]=dgt[\bar{n}]+1$
上面的递推式虽然都只针对某一个数$n$,但完全可以逐层推广到之前所有数的和
使原来的$res,suf,dgt$分别表示$\sum res,\sum suf,\sum dgt$,$a$表示数的个数
再用第二维的$0/1$表示是否达到上界,算是数位$dp$的常规套路
剩下的递归式还是看代码吧……
Code:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=1e5+10,MOD=20130427; int B,l1,l2,dat1[MAXN],dat2[MAXN]; ll pre[MAXN],res[MAXN][2],a[MAXN][2],suf[MAXN][2],dgt[MAXN][2]; ll solve(int *dat,int l) { memset(res,0,sizeof(res));memset(dgt,0,sizeof(dgt)); memset(a,0,sizeof(a));memset(suf,0,sizeof(suf)); a[l+1][0]=1; for(int i=l;i;i--) { int cur=(i==l)?0:B; a[i][0]=a[i+1][0]; a[i][1]=((cur-1)+a[i+1][1]*B+a[i+1][0]*dat[i])%MOD; dgt[i][0]=(dgt[i+1][0]+a[i+1][0])%MOD; dgt[i][1]=((cur-1)+(dgt[i+1][1]+a[i+1][1])*B%MOD+(dgt[i+1][0]+a[i+1][0])*dat[i]%MOD)%MOD; suf[i][0]=(suf[i+1][0]*B+dgt[i][0]*dat[i])%MOD; suf[i][1]=(pre[cur]+(suf[i+1][1]*B%MOD*B%MOD+(dgt[i+1][1]+a[i+1][1])*pre[B]%MOD)+ (suf[i+1][0]*B*dat[i]%MOD+dgt[i][0]*pre[dat[i]]%MOD))%MOD; res[i][0]=(res[i+1][0]+suf[i][0])%MOD; res[i][1]=(res[i+1][0]*dat[i]%MOD+res[i+1][1]*B%MOD+suf[i][1])%MOD; } return (res[1][0]+res[1][1])%MOD; } int main() { scanf("%d",&B); for(int i=1;i<=B;i++) pre[i]=(pre[i-1]+i-1)%MOD; scanf("%d",&l1); for(int i=l1;i>=1;i--) scanf("%d",&dat1[i]); scanf("%d",&l2); for(int i=l2;i>=1;i--) scanf("%d",&dat2[i]); for(int i=1;i<=l1;i++) if(dat1[i]){dat1[i]--;break;} else dat1[i]=B-1; if(!dat1[l1]) l1--; printf("%lld",(solve(dat2,l2)-solve(dat1,l1)+MOD)%MOD); return 0; }