bzoj 1833 [ZJOI2010]count 数字计数(数位DP)
【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=1833
【题意】
统计[a,b]区间内各数位出现的次数。
【思路】
设f[i][j][k]表示i位数,最高位为j,数位k出现的次数,则有递推式:
f[i][j][k]=sigma{ f[i-1][l][k] ,0<=l<=9 } + 10^(i-1)
第一项统计的是i-1位数中k的出现次数,后一项统计的是最高位k的出现数目。
将查询差分,对于一个数,先统计长度不超过len的,再统计长度=len但最高位比a[len]小的,再统计长度为len且最高位为a[len]的。
注意最后一项的统计,假设n为4321,我们要统计的就是:
4000..4299
4300..4319
4320..4321
假设我们要统计4000..4299,则有;
ans[k]+=f[3][3][k]
ans[4]+=321+1
【代码】
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define FOR(a,b,c) for(int a=b;a<=c;a++) 5 using namespace std; 6 7 typedef long long ll; 8 const int N = 25; 9 10 struct Node{ 11 ll a[N]; 12 Node() { 13 memset(a,0,sizeof(a)); 14 } 15 ll& operator[] (const int& x) { 16 return a[x]; 17 } 18 }; 19 Node operator + (const Node& x,const Node& y) { 20 Node tmp; 21 FOR(i,0,9) tmp.a[i]=x.a[i]+y.a[i]; 22 return tmp; 23 } 24 25 int len,a[N]; 26 ll e[N]; Node f[N][N]; 27 28 void init(ll n) 29 { 30 len=0; 31 while(n) { 32 a[++len]=n%10; 33 n/=10; 34 } 35 FOR(i,0,9) f[1][i][i]=1; 36 FOR(i,2,14) FOR(j,0,9) { 37 FOR(k,0,9) 38 f[i][j]=f[i][j]+f[i-1][k]; 39 f[i][j][j]+=e[i-1]; 40 } 41 } 42 Node calc(ll n) 43 { 44 Node ans; 45 if(!n) return ans; 46 memset(f,0,sizeof(f)); 47 init(n); 48 FOR(i,1,len-1) FOR(j,1,9) 49 ans=ans+f[i][j]; 50 FOR(i,1,a[len]-1) ans=ans+f[len][i]; 51 n%=e[len-1]; 52 ans[a[len]]+=n+1; 53 for(int i=len-1;i;i--) { 54 for(int j=0;j<a[i];j++) ans=ans+f[i][j]; 55 n%=e[i-1]; 56 ans[a[i]]+=n+1; 57 } 58 return ans; 59 } 60 61 int main() 62 { 63 e[0]=1; 64 FOR(i,1,14) e[i]=e[i-1]*10ll; 65 ll x,y; 66 scanf("%lld%lld",&x,&y); 67 Node ans1=calc(y) , ans2=calc(x-1); 68 FOR(i,0,8) printf("%lld ",ans1[i]-ans2[i]); 69 printf("%lld\n",ans1[9]-ans2[9]); 70 return 0; 71 }
posted on 2016-03-16 14:26 hahalidaxin 阅读(239) 评论(1) 编辑 收藏 举报