uva11361 特殊数的数量(数位dp)

题目传送门

题目大意:给你一个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;
	}
}

原文地址:https://www.cnblogs.com/mountaink/p/9536724.html