[BZOJ] 1833: [ZJOI2010]count 数字计数
1833: [ZJOI2010]count 数字计数
Time Limit: 3 Sec Memory Limit: 64 MBSubmit: 3784 Solved: 1663
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
Sample Output
HINT
30%的数据中,a<=b<=10^6;
100%的数据中,a<=b<=10^12。
Source
Analysis
数位动规模板题
蒟蒻的数位动规之旅可谓艰辛qwq
四处扒题解搞模板
最后还是用的自己的诡异表示AC
= =
首先状态表示 DP[ len ] 表示长度为 len 的,额,数码串所有状态包含的单个数码的数量
你看,如果不计算前导零的话,其实每个数码(0,1,2,3...)都是一样的
十个数码的数量都是重复数据
所以将前导零暂且扔掉,只储存指定长度的数码数量
那么引入一个 Pow[ len ] ,你看,如果给一个指定的数码串再多加一位,那么多加的那一位会使数量增益个,,,额
原来的数码串是形如 4357594 这样的 7 位长的
那么我们增加第 8 位时,原来的排列并不会改变,但是会增加 0435... 1435... 2435... 等
这样,填进第 8 位的数码就有 107 的增加量
那么定义 Pow[ i ] = 10i-1
就得到了 DP[ i ] = DP[ i-1 ] * 10 + Pow[ i ]
以下为对代码的解释
那么我们考虑这个数位动规的实质,它的实质就是对一棵类似字典树的东西计数,深度即长度
那么用上面那种极其简单的DP表示,我们可以解决掉一棵完整的子树,却不能很好的处理形如 85 这种不能单纯用长度来计数的数字
那么对于这种不完整子树,我们只要一层层往下处理就行了
如上图,我们可以用 DP 直接统计掉第二层的 0 - 7 以及第三层的 0 - 4
但是处于边界的 8 和 5 ... = =
我们这样考虑
用 DP 能够处理掉 8 左边的部分,那么 8 本身的答案对应到实际是这样的:
80 81 82 83 84 85
也就是,8 这棵树有多少叶子结点,他就有多少增益
那么拿 85 减去前面计数过的叶子结点,就是 8 的增益了
之后是前导零
前导零在树中是这样子的:
反而前导零比较容易处理
每一层都给 0 的计数减去一个 Pow[ len ] 即可
注意只有一位数的 0 是算数的,所以需要提前给 0 增加
Code
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 5 long long Pow[20],DP[20],a,b; 6 7 struct data{ 8 long long d[20]; 9 data(){ for(int i = 0;i < 20;i++) d[i] = 0;} 10 }; 11 12 long long getlen(long long &x){ 13 long long ret = 0; for(long long i = x;i;i /= 10) ret++; 14 return ret; 15 } 16 17 data conc(long long x){ 18 x++; 19 long long dig[20] = {0},lenx = 0; 20 data ans; 21 if(!x) return ans; 22 23 for(long long i = x;i;i /= 10) 24 dig[lenx++] = i%10; 25 26 ans.d[0]++; 27 for(long long p = lenx-1;p >= 0;p--){ 28 ans.d[0] -= Pow[p+1]; 29 for(long long i = 0;i < dig[p];i++){ 30 ans.d[i] += Pow[p+1]; 31 for(long long j = 0;j <= 9;j++){ 32 ans.d[j] += DP[p]; 33 } 34 }x -= Pow[p+1]*dig[p]; 35 ans.d[dig[p]] += x; 36 } 37 return ans; 38 } 39 40 int main(){ 41 Pow[0] = Pow[1] = 1; 42 for(long long i = 2;i <= 15;i++) Pow[i] = Pow[i-1]*10; 43 44 scanf("%lld%lld",&a,&b); 45 long long lena = getlen(a),lenb = getlen(b); 46 47 DP[1] = 1; 48 for(long long i = 2;i <= lenb+1;i++) DP[i] = DP[i-1]*10+Pow[i]; 49 50 data ret1 = conc(a-1); 51 data ret = conc(b); 52 53 for(long long i = 0;i <= 9;i++) 54 if(!i) printf("%lld",ret.d[i]-ret1.d[i]); 55 else printf(" %lld",ret.d[i]-ret1.d[i]); 56 57 return 0; 58 }