博主最近一直在积累组合计数方面的经验 , 这一次是数位dp......感谢Remilia's文章的帮助
提示:
1. 本题思路是利用数位进行计数 , 但网上也有容斥原理和AC动归的做法(其实博主一开始也想用容斥原理做的)
2. 这种经典的两端点区间的问题 , 可以分化成两个从0开始的区间的差集
3. 状态数组的意义: d[ i ][ j ][ k ][ p ][ q ] 中 , 处理完第i位 , 此时末两位是j , 是否已经比上界小(k=0 , 1) , 是否出现子串(p=0 , 1) , 是否已经有一位不是0(q=0 , 1)
注意: 转移方程可以自己好好考虑 , 但博主强烈建议使用向前刷表的方式
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <string> #include <vector> #include <deque> #include <stack> #include <algorithm> using namespace std; // dp version typedef long long ll; ll A, B , N; int a[20] , an; ll d[20][100][2][2][2]; // instore the last 2 bits , 2-bit-strategy ll solve(ll A) // we dont consider 0 here { if(A<0) return 0; if(A==0) { if(N==0) return 1; return 0; } ll nn = N , base=1; while(nn) base*=10 , nn/=10; if(N==0) base=10; an=0; memset(a, 0, sizeof(a)); while(A) a[++an] = A%10 , A/=10; an = max(an , 1); reverse(a+1, a+1+an); memset(d, 0, sizeof(d)); d[0][0][0][0][0] = 1; for(int i=0;i<an;i++) for(int j=0;j<100;j++) for(int k=0;k<2;k++) for(int p=0;p<2;p++) for(int q=0;q<2;q++) // push forward { ll& res = d[i][j][k][p][q]; if(!res) continue; for(int jj=0;jj<10;jj++) // what I add for the next blank { if(k==0 && jj>a[i+1]) continue; int kk=-1 , pp=-1 , qq=-1; kk=k; if(k==0 && jj<a[i+1]) kk = 1; pp = p; if(p==0 && (j*10+jj)%base==N) pp = 1; qq = q; if(q==0 && jj) qq = 1; if(qq==0) pp = 0;// in order to prevent the 0 condition d[i+1][(j%10)*10+jj][kk][pp][qq] += res; } } ll res =0; for(int j=0;j<100;j++) for(int k=0;k<2;k++) res+= d[an][j][k][1][1]; if(N==0) res++; return res; } int main(int argc, char *argv[]) { while(cin>>A>>B>>N && A!=-1) cout<<solve(B)-solve(A-1)<<endl; return 0; }