题目大意:给你一个n-m的区间,问你这个闭区间内的特殊数有几个,特殊数的要求是 数的本身 和 各位数字之和 mod k 等于0.
思路:刚接触数位dp,看了网上的题解,说用dp[i][j][s]表示,总共有i位,数字本身mod k为j,各位数之和mod k为s的数量,然后状态转移方程是dp[i][(j+x)%k][(s*10+x)%k]+=dp[i][j][s],第一次看这方程感觉好有道理,然后看代码发现数位dp最重要的还是计数原理,过了好久才a了这道题。主要的思路放在代码注释里了。
#include<iostream> #include<algorithm> #include<cstdlib> #include<sstream> #include<cstring> #include<bitset> #include<cstdio> #include<string> #include<deque> #include<stack> #include<cmath> #include<queue> #include<set> #include<map> #define INF 0x3f3f3f3f #define CLR(x,y) memset(x,y,sizeof(x)) #define LC(x) (x<<1) #define RC(x) ((x<<1)+1) #define MID(x,y) ((x+y)>>1) using namespace std; typedef pair<int,int> pii; typedef long long ll; const double PI=acos(-1.0); int fact[10]= {1,1,2,6,24,120,720,5040,40320,362880}; const int maxn = 100005; ll dp[11][90][90]; int po[11]; void brea(ll n) { stack<int >s; while(n>0) { s.push(n%10); n/=10; } while(!s.empty()) { po[++po[0]]=s.top(); s.pop(); } //此时po[0]存的是位数 然后其他的就是从第一位到最后一位了 } ll cs(ll n,ll k) { memset(dp,0,sizeof(dp)); memset(po,0,sizeof(po)); brea(n); //把n拆开放到数组中 int ans=0,ant=0; for(int i=1; i<=po[0]; i++) { for(int j=0; j<k; j++) { //处理 0-69 因为只有十位上是0-6时 s才可以取到0和9 for(int m=0; m<k; m++) { for(int s=0; s<=9; s++) { dp[i][(j+s)%k][(m*10+s)%k]+=dp[i-1][j][m]; } } } for(int j=0; j<po[i]; j++) //处理70 71 72 而73没有被计算 { //由于这里是 < 所以在处理第一位的时候 7并没有被计算 所以下一次循环时 只有0-6被处理了 dp[i][(ans+j)%k][(ant*10+j)%k]++; } ans=(ans+po[i])%k; //各位数之和mod ant=(ant*10+po[i])%k; //本身mod } if(ans==0&&ant==0)dp[po[0]][0][0]++; //处理73 return dp[po[0]][0][0]; } int main() { int t; cin>>t; while(t--) { ll n,m,k; cin>>n>>m>>k; if(k>90) //由于n和m最多10位 最大也就10个9 如果k为91 那就肯定不存在这样的数 { cout<<0<<endl; continue; } cout<<cs(m,k)-cs(n-1,k)<<endl; } }