山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

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编辑  收藏  举报