数位DP练习题

文章目录

  • T1 [ZJOI2010]数字计数
  • T2 [SCOI2009]windy数
  • T3 花神的数论题
  • T4 [CQOI2016]手机号码
  • T5 [HAOI2010]计数
  • T6 吉哥系列故事——恨7不成妻
  • T7 不要62
  • T8 吉利数字

T1 [ZJOI2010]数字计数

题目描述
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

输入输出格式
输入格式:
输入文件中仅包含一行两个整数a、b,含义如上所述。

输出格式:
输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

输入输出样例
输入样例#1: 复制
1 99
输出样例#1: 复制
9 20 20 20 20 20 20 20 20 20
说明
30%的数据中,a<=b<=10^6;

100%的数据中,a<=b<=10^12。

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu2602 数字计数 
Algorithm:数位DP 
************************/
//核心思想:打表? 
//好吧我知道为什么过不了了
//数组开小了,访问越界 
#include

using namespace std;

const int maxn=15;

typedef long long LL;

LL a,b;
LL cnt[15],ans[5][15],num[5][15];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("count0.in","r",stdin);
}

void init(){
	cnt[0]=0;
	LL w=1;
	for(int i=1;i<=13;++i) cnt[i]=cnt[i-1]*10+w,w*=10;///////////
	//cnt[i]表示有 i 位数,各个数字出现的次数
	//此处 0 可以在最高位 
	
	LL x=a;
	if(a==0) num[1][0]=1;
	else if(a<0) a=0;//特判a=1或a=0时 
	while(x){
		++num[1][0];
		num[1][num[1][0]]=x%10;
		x/=10;
	}
	
	x=b;
	if(b==0) num[2][0]=1;
	else if(b<0) b=0;

	while(x){
		++num[2][0];
		num[2][num[2][0]]=x%10;
		x/=10;
	}

}

void readdata(){
	read(a);read(b);
	--a;//用 ans[0~b]-ans[0~a-1] 求解 
	init();
}

void dp(LL r,LL id){
	LL t=0,w=1;
	for(LL i=1;i<=num[id][0];++i){
		
		t+=num[id][i]*cnt[i-1];
		
		for(LL j=0;j<num[id][i];++j) 
			ans[id][j]+=w;
		
		ans[id][num[id][i]]+=r%w+1;
		
		w*=10;
	}
	
	for(int i=0;i<=9;++i) ans[id][i]+=t;

	t=1;
	for(int i=1;i<num[id][0];++i){
		t*=10;
		ans[id][0]-=t;//减去 0 在最高位的情况 
	}
	
}

void work(){
	dp(a,1);
	dp(b,2);
	for(int i=0;i<=9;++i){
		/*put(ans[2][i]-ans[1][i]);
		putchar(' ');*/
		printf("%lld ",ans[2][i]-ans[1][i]);
	}
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

方法二:

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu2602
Algorithm:数位 DP DFS 
************************/
#include
//这道题如果从 0 开始的话还要加个特判 
using namespace std;

const int maxn=35;

typedef long long LL;
LL a,b;
LL num[15],dp[20][20];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("count.in","r",stdin);
}

LL dfs(LL dig,int limit,int lead,LL sum,LL pos){
	//pos 位数
	//dig 要找的数字
	//limit 这一位是否有限制 
	//lead 是否有前导0  如果lead==0 那么有前导0
	//sum : 搜到pos位时出现了多少个dig 
	if(!pos) return sum;
	
	if(!limit && lead && dp[pos][sum]!=-1) return dp[pos][sum];
	//没上限且没有前导0 
	//主要防止0算错   eg.0000 0010 
	LL ans=0;
	
	int top=limit ? num[pos] : 9;
	
	for(int i=0;i<=top;++i)
		ans+=dfs(dig,limit&&i==top,i||lead,sum+((lead||i)&&(i==dig)),pos-1);
	
	if(!limit && lead) dp[pos][sum]=ans;
	
	return ans;
}

LL work(LL dig,LL r){
	memset(num,0,sizeof(num));
	memset(dp,-1,sizeof(dp));
	LL x=r;
	
	if(!x) num[0]=1;
	while(x){
		++num[0];
		num[num[0]]=x%10;
		x/=10;
	}
	
	return dfs(dig,1,0,0,num[0]);
}

int main(){
//	file();
	read(a);read(b);
	for(LL i=0;i<=9;++i){
		put(work(i,b)-work(i,a-1));
		putchar(' ');
	}
	return 0;
}

T2 [SCOI2009]windy数

题目描述
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,

在A和B之间,包括A和B,总共有多少个windy数?

输入输出格式
输入格式:
包含两个整数,A B。

输出格式:
一个整数

输入输出样例
输入样例#1: 复制
1 10
输出样例#1: 复制
9
输入样例#2: 复制
25 50
输出样例#2: 复制
20
说明
100%的数据,满足 1 <= A <= B <= 2000000000 。

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu 2657windy
Algorithm:数位DP 
************************/

#include

using namespace std;

const int maxn=15;

int a,b,ans[5];
int cnt[15][15],cnt1[15],num[5][15];
//cnt[位数][最高位的数值]=windy数个数
//cnt1[位数]=从0~这个位数的最大值中windy数的个数 
template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("windy.in","r",stdin);
}

void init(){
	cnt[0][0]=1;//初始化,要加0 
	cnt1[0]=1;
	for(int i=0;i<=9;++i) cnt[1][i]=1;
	cnt1[1]=10;
	
	for(int l=2;l<=12;++l){
		for(int k=0;k<=9;++k){
			for(int i=0;i<=9;++i)
				if(abs(i-k)>=2) cnt[l][k]+=cnt[l-1][i];//我们把0当做 可以做最高位的数 
			if(k) cnt1[l]+=cnt[l][k];//不加0,0不做最高位 
		}
		cnt1[l]+=cnt1[l-1];//加上以前位数的个数 
	}
		
	int x=a;
	if(!a) num[1][0]=1;
	while(x){
		++num[1][0];
		num[1][num[1][0]]=x%10;
		x/=10;
	}

	x=b;
	if(!b) num[2][0]=1;
	while(x){
		++num[2][0];
		num[2][num[2][0]]=x%10;
		x/=10;
	}
	
}

void dp(int r,int id){
	num[id][num[id][0]+1]=-100;//防止误把第一位pass掉 
	ans[id]+=cnt1[num[id][0]-1];
	for(int i=num[id][0];i>=1;--i){
		if(i==num[id][0]){
			for(int j=1;j<num[id][i];++j) ans[id]+=cnt[i][j];
		}else {
			for(int j=0;j<num[id][i];++j)
			if(abs(num[id][i+1]-j)>=2) ans[id]+=cnt[i][j];
			
			if(i==1&&abs(num[id][i+1]-num[id][i])>=2) ans[id]++;//特判 b 
		}
		if(abs(num[id][i+1]-num[id][i])<2) break;//!!如果这一位与上一位相差小于2,直接结束 
	}
}

void work(){
	dp(a,1);
	dp(b,2);
	printf("%d",ans[2]-ans[1]);
}

int main(){
//	file();
	read(a);read(b);
	--a;
	init();
	work();
	return 0;
}

方法二:

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu2657
Algorithm:数位 DP DFS 
************************/
#include

using namespace std;

const int maxn=35;

typedef long long LL;
int a,b;
int num[15],dp[20][20];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("windy.in","r",stdin);
}

int dfs(int pos,int now,int limit,int lead){
	
	if(pos==0) return 1;
	if(!limit && lead && dp[pos][now]!=-1) return dp[pos][now];
	
	int ans=0;
	
	int top=limit ? num[pos] : 9;
	
	for(int i=0;i<=top;++i){
		if(lead==0||abs(now-i)>=2) ans+=dfs(pos-1,i,limit&&(i==top),lead||i);
	}
	if(!limit&&lead) dp[pos][now]=ans;
	return ans;
}
		
int work(int r){
	memset(num,0,sizeof(num)); 
//	memset(dp,-1,sizeof(dp));
	
	int x=r;
	if(!x) num[0]=1;
	while(x){
		num[0]++;
		num[num[0]]=x%10;
		x/=10;
	}
	
	return dfs(num[0],0,1,0);
}

int main(){
//	file();
	memset(dp,-1,sizeof(dp));
	read(a);read(b);
	put(work(b)-work(a-1));
	return 0;
}

T3 花神的数论题

题目背景
众所周知,花神多年来凭借无边的神力狂虐各大 OJ、OI、CF、TC …… 当然也包括 CH 啦。

题目描述
话说花神这天又来讲课了。课后照例有超级难的神题啦…… 我等蒟蒻又遭殃了。 花神的题目是这样的:设 \text{sum}(i)sum(i) 表示 ii 的二进制表示中 11 的个数。给出一个正整数 NN ,花神要问你 \prod_{i=1}^{N}\text{sum}(i)∏
i=1
N
​ sum(i) ,也就是 \text{sum}(1)\sim\text{sum}(N)sum(1)∼sum(N) 的乘积。

输入输出格式
输入格式:
一个正整数 N。

输出格式:
一个数,答案模 10000007 的值。

输入输出样例
输入样例#1: 复制
3
输出样例#1: 复制
2
说明
对于 100% 的数据,N≤10^15

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu4317
Algorithm:数位 DP 
************************/

#include

using namespace std;

const int maxn=1e6+5;
const int mod=1e7+7;

typedef long long LL;

LL n;
LL dp[65][65],num[65],ans[65];
//ans[i]表示 1~N中的含有i个1的数的个数 
template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("flower.in","r",stdin);
}

LL dfs(int pos,int cnt,int limit,int sum){
	//cnt是当前搜到的1的个数
	//sum是所需的1的个数 
	if(!pos) return cnt==sum;
	
	if(!limit && dp[pos][cnt]!=-1) return dp[pos][cnt];
	
	LL tot=0;
	int top=limit ? num[pos] : 1;
	for(int i=0;i<=top;++i)
		tot+=dfs(pos-1,cnt+(i&1),limit&&(i==top),sum);
	
	if(!limit) dp[pos][cnt]=tot;
	
	return tot;
}

LL quickpower(LL i,LL j){
	LL anss=1,base=i;
	while(j){
		if(j&1) anss=anss*base%mod;
		base=base*base%mod;//记得及时取模 
		j>>=1;
	}
	return anss;
}

void work(){
	if(!n) num[0]=1;
	while(n){
		++num[0];
		num[num[0]]=(n&1);
		n>>=1;
	}
	
	for(int i=1;i<=num[0];++i){
		memset(dp,-1,sizeof(dp));
		ans[i]=dfs(num[0],0,1,i);
	}
	
	LL tot=1;
	for(LL i=2;i<=num[0];++i){
		tot=tot*quickpower(i,ans[i])%mod;
	}
	
	put(tot);
}

int main(){
//	file();
	read(n);
	work();
	return 0;
}

T4 [CQOI2016]手机号码

题目描述
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。

工具需要检测的号码特征有两个:号码中要出现至少 33 个相邻的相同数字;号码中不能同时出现 88 和 44。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。

手机号码一定是 1111 位数,前不含前导的 00。工具接收两个数 LL 和 RR,自动统计出 [L,R][L,R] 区间内所有满足条件的号码数量。LL 和 RR 也是 1111 位的手机号码。

输入输出格式
输入格式:
输入文件内容只有一行,为空格分隔的 22 个正整数 L,RL,R。

输出格式:
输出文件内容只有一行,为 11 个整数,表示满足条件的手机号数量。

输入输出样例
输入样例#1: 复制
12121284000 12121285550
输出样例#1: 复制
5
说明
样例解释:满足条件的号码: 12121285000、 12121285111、 12121285222、 12121285333、 12121285550。

数据范围:10000000000<=L<=R<100000000000 。

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu4214
Algorithm:数位 DP 
************************/

#include

using namespace std;

typedef long long LL;

LL L,R;
LL num[25];
LL dp[20][13][11][3][3][3];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("phone.in","r",stdin);
}

LL dfs(int pos,int limit,int judge,int cnt,int mcnt,int now){
	
	if(!pos)
		if(mcnt >= 3) return 1;
		else return 0;
		
	if(!limit && dp[pos][cnt][now][mcnt>=3][((judge & (1<<4))) == (1<<4)][((judge & (1<<8))) == (1<<8)] != -1) 
		return dp[pos][cnt][now][mcnt>=3][((judge & (1<<4))) == (1<<4)][((judge & (1<<8))) == (1<<8)];
	
	LL ans = 0;
	int top = limit ? num[pos] : 9;
	
	for(int i=0;i<=top;++i){
		if(((judge | (1 << i)) & ((1 << 4) | (1 << 8))) == ((1 << 4) | (1 << 8))) continue;
		int x = (now == i) ? (cnt + 1) : 1;
		ans+=dfs(pos - 1,limit && (i == top),judge | (1 << i),x,max(x,mcnt),i);
	}
	
	if(!limit) dp[pos][cnt][now][mcnt>=3][((judge & (1 << 4))) == (1 << 4)][((judge & (1 << 8))) == (1 << 8)] = ans;
	
	return ans;
	
}
		
LL work(LL r){
	memset(num,0,sizeof(num));
//	memset(dp,-1,sizeof(dp));
	
	LL x=r;
	while(x){
		num[0]++;
		num[num[0]] = x % 10;
		x /= 10;
	}
	
	if(num[0]<=10) num[0]++;//主要防止1000000000减了1之后就少了一位,而这个在算时算了前导0,会出错 
	
	return dfs(num[0],1,0,0,0,0);
}

int main(){
//	file();
memset(dp,-1,sizeof(dp));
	read(L);read(R);
	put(work(R) - work(L-1));
	return 0;
}

T5 [HAOI2010]计数

题目描述
你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数。比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等。

现在给定一个数,问在这个数之前有多少个数。(注意这个数不会有前导0).

输入输出格式
输入格式:
只有1行,为1个整数n.

输出格式:
只有整数,表示N之前出现的数的个数。

输入输出样例
输入样例#1: 复制
1020
输出样例#1: 复制
7
说明
n的长度不超过50,答案不超过2^63-1.

/**************************
User:Mandy.H.Y
Language:c++
Problem:luogu2518
Algorithm:数位DP 
**************************/

#include

//删0就相当于把0放在最前面
//所以可以求组合………… 

using namespace std;

typedef long long LL;

const int maxn = 35;

char c;
LL num[55],cnt[11],C[66][66];
//C[i][j]是组合数
//cnt[i] 代表num中有cnt[i]个数字i 

template<class T>inline void read(T &x){
	x = 0;bool flag = 0;char ch = getchar();
	while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); 
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); 
	if(flag) x = -x;
}

template<class T>void putch(const T x){
	if(x > 9) putch(x / 10);
	putchar(x % 10 | 48);
}

template<class T>void put(const T x){
	if(x < 0) putchar(' '),putch(-x);
	else putch(x);
}

void file(){
	freopen("count.in","r",stdin);
}

void init(){//组合数,杨辉三角是个好东西 
	for(int i = 0;i <= 60;++ i) C[i][0] = (LL)1;
	for(int i = 1;i <= 60;++ i)
		for(int j = 1;j <= 60;++ j)
			C[i][j]=C[i-1][j-1]+C[i-1][j];
}

void readdata(){
		while(~scanf("%c",&c)){
		if(!isdigit(c)) continue;
		num[0] ++;
		num[num[0]] = c ^ 48;
		cnt[(c ^ 48)] ++;
	}
}

LL dfs(int pos,int limit){
	
	if(pos == num[0] + 1) return 0;//这个数本身不算 
	
	LL ans = 1;
	
	if(!limit){
//如果没有限制,那么剩下的数有 num[0] - pos + 1个位置
//因为有重复的数字,如果直接用乘法原理会爆long long,边乘边除太麻烦
//我们可以一组一组放,比如先放cnt[0]个 0,则方案有C[mcnt][cnt[0]]种 
//放完0后又放1,则现在还剩下mcnt-cnt[0]个位置,有 C[mcnt-cnt[0]][cnt[1]]种方法
//现在放完0和1,共 C[mcnt][cnt[0]] * C[mcnt-cnt[0]][cnt[1]]种方法 
		int mcnt = num[0] - pos + 1;
		for(int i = 0;i <= 9;++ i){
			if(cnt[i]) ans *= C[mcnt][cnt[i]];//有i才放 
			mcnt-=cnt[i];
		}
		return ans;
	}
	
	ans = 0;
	int top = limit ? num[pos] : 9;
	
	for(int i = 0;i <= top;++ i){
		if(!cnt[i]) continue;
		cnt[i]--;
		ans += dfs(pos + 1,limit && i == top);
		cnt[i]++;
	}
	
	return ans;
}

int main(){
//	file();
	init();
	readdata();
	put(dfs(1,1));
	return 0;
}

T6 吉哥系列故事——恨7不成妻

Problem Description

单身!
  依然单身!
  吉哥依然单身!
  DS级码农吉哥依然单身!
  所以,他生平最恨情人节,不管是214还是77,他都讨厌!
  
  吉哥观察了214和77这两个数,发现:
  2+1+4=7
  7+7=72
  77=7
11
  最终,他发现原来这一切归根到底都是因为和7有关!所以,他现在甚至讨厌一切和7有关的数!

什么样的数和7有关呢?

如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
  1、整数中某一位是7;
  2、整数的每一位加起来的和是7的整数倍;
  3、这个整数是7的整数倍;

现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。

Input

输入数据的第一行是case数T(1 <= T <= 50),然后接下来的T行表示T个case;每个case在一行内包含两个正整数L, R(1 <= L <= R <= 10^18)。

Output

请计算[L,R]中和7无关的数字的平方和,并将结果对10^9 + 7 求模后输出。

Sample Input

3
1 9
10 11
17 17

Sample Output

236
221
0

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu4317
Algorithm:数位 DP 
************************/

#include

using namespace std;

typedef long long LL;

const int maxn=1e6+5;
const long long mod=(LL)1e9+7;

LL n,L,R;
LL num[25],power[25];

struct node{
	LL cnt,sum,tot;
	node(){cnt=-1,sum=0,tot=0;}
	node(LL c,LL s,LL t) : cnt(c),sum(s),tot(t){}
}dp[25][10][10];

//dp[位数][当前数%7余数][当前各位之和%7]
//cnt:从1到当前位的与7无关的数的个数
//sum :从1到当前位的与7无关的数的和 
//tot :从1到当前位的与7无关的数的平方和 

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("test1.in","r",stdin);
}

node dfs(int pos,int limit,int res,int sum){
//res:当前数%7余数
//sum :当前各位之和%7 

	if(!pos) {
		if(sum && res ) return node(1,0,0);
		else return node(0,0,0);
	}
	
	if( !limit && dp[pos][res][sum].cnt!=-1 ) return dp[pos][res][sum];
	
	LL top=limit ? num[pos] : 9;
	
	node y;
	y.cnt=0,y.sum=0,y.tot=0;
	
	for(int i=0;i<=top;++i){
		
		if(i!=7) {//除去含7的数 
			node x;
			
			x=dfs(pos-1,limit && i==top,(res * 10 + i) % 7,(sum + i) % 7);
			
			LL cur = i * power[pos-1] % mod;
			
			y.cnt=(y.cnt + x.cnt) % mod;
			
			y.sum=(y.sum + cur * x.cnt % mod + x.sum) % mod;
			
			y.tot=((y.tot + x.tot) % mod + cur * cur % mod * x.cnt % mod+ 2 * cur  % mod * x.sum % mod/*这里不乘cnt*/) % mod;
			//完全平方 
		}
	}
	
	if( !limit )  dp[pos][res][sum]=y;
	
	return y;
}
		
LL work(LL r){
	memset(num,0,sizeof(num));
//	memset(dp,-1,sizeof(dp));
	
	LL x=r;
	if(!x) num[0]=1;
	while(x){
		++num[0];
		num[num[0]]=x % 10;
		x /= 10;
	}
	
	node y = dfs(num[0],1,0,0);
	return y.tot % mod;
}

int main(){
//	file();
	read(n);
	power[0] = 1;
	memset(dp,-1,sizeof(dp));
	for(int i = 1;i <= 20;++i) power[i]=(power[i - 1] * 10) % mod;//10的幂 
	while(n--){
		read(L);read(R);
		put(((work(R) - work(L - 1))%mod + mod) % mod);
		putchar('\n');
	}
	return 0;
}

T7 不要62

Problem Description

杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
Input
输入的都是整数对n、m(0 Output
对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
Sample Input
1 100

0 0
Sample Output
80

/************************
User:Mandy.H.Y
Language:c++
Problem:hdu2809
Algorithm:数位 DP 
************************/

#include

using namespace std;

const int maxn=1e6+5;

int n,m;
int num[20],dp[20][15];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("62.in","r",stdin);
}

int dfs(int pos,int limit,int now){
	
	if(!pos) return 1;
	if(!limit && dp[pos][now]!=-1) return dp[pos][now];
	
	int ans=0;
	int top=limit ? num[pos] : 9;
	for(int i=0;i<=top;++i){
		if(i==4||(now==6&&i==2)) continue;
		ans+=dfs(pos-1,limit&&(i==top),i);
	}
	
	if(!limit) dp[pos][now]=ans;
	
	return ans;
}

int work(int r){
	memset(num,0,sizeof(num));
//	memset(dp,-1,sizeof(dp));	
	
	int x=r;
	if(!x) num[0]=1;
	while(x){
		num[0]++;
		num[num[0]]=x%10;x/=10;
	}
	
	return dfs(num[0],1,0);
}

int main(){
//	file();
		memset(dp,-1,sizeof(dp));
	while(scanf("%d%d",&n,&m)){
		if((!n)&&(!m)) break;
		put(work(m)-work(n-1));
		putchar('\n');
	} 
	return 0;
}

T8 吉利数字

【题目描述】

中国人喜欢数字6和8。特别地,一些人喜欢满足含有特定个数6和8的数。现在请求出,在区间[L,R]之间的第K大的含有X个6和Y个8的数。

【输入】

输入的第一行包括4个数字,L,R,X,Y。

接下来的一行给出该组数据的询问数Q。

接下来Q行中,每行有一个整数K。

【输出】

对于某个询问,输出一行,为对应的第K大的数。如果不存在这个数则输出“That’s too bad!”

【输入样例】

1 1000 1 1

10

1

2

3

4

5

6

7

8

9

100

【输出样例】

68

86

168

186

268

286

368

386

468

That’s too bad!

【数据范围】

对于30%的数据,1<=L<=R<=100000

对于100%的数据,1<=L<=R<=10^18

对于100%的数据,1<=X,Y<=18, 1<=Q<=30

/**************************
User:Mandy.H.Y
Language:c++
Problem:mzoj 1486
Algorithm:数位DP 
**************************/

//用到了那么一丢丢 恨7不成妻 的思想,不过这道题简单一点 

//直接二分求个数再维护最大值即可

#include

using namespace std;

typedef long long LL;

const int maxn = 35;

LL L,R,X,Y,Q,K;
LL num[20],power[21];

struct node{
	LL cnt,mans;
	
	node(){cnt = -1;mans = 0;}
	node(LL c,LL m) : cnt(c),mans(m){}
	
}dp[20][20][20];

template<class T>inline void read(T &x){
	x = 0;bool flag = 0;char ch = getchar();
	while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); 
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); 
	if(flag) x = -x;
}

template<class T>void putch(const T x){
	if(x > 9) putch(x / 10);
	putchar(x % 10 | 48);
}

template<class T>void put(const T x){
	if(x < 0) putchar(' '),putch(-x);
	else putch(x);
}

void file(){
	freopen("0.in","r",stdin);
}

node dfs(int pos,int limit,int six,int eight){
	
	if(!pos) return node((six == X) && (eight == Y),0);
	
	if(!limit && dp[pos][six][eight].cnt != -1) return dp[pos][six][eight];
	
	node ans;
	ans.cnt = 0,ans.mans = 0;
	LL top = limit ? num[pos] : 9;
	
	for(int i = 0;i <= top;++i){
		
		if(six + (i == 6) > X || eight + (i == 8) > Y) continue;
		
		node x = dfs(pos - 1,limit && (i == top),six + (i == 6),eight + (i == 8));
		
		ans.cnt += x.cnt;
		
		if (x.cnt) ans.mans = max(ans.mans,i * power[pos - 1] + x.mans);
	}
	
	if(!limit) dp[pos][six][eight] = ans;
	
	return ans;
}

node work(LL r){//记得开LL啊!!! 
	memset(num,0,sizeof(num));
//	memset(dp,-1,sizeof(dp));
	if(!r) num[0] = 1;
	while(r){
		num[0]++;
		num[num[0]] = r % 10;
		r /= 10;
	}
	
	return dfs(num[0],1,0,0);
}

int main(){
//	file();
	//memset(dp,-1,sizeof(dp));
	power[0] = 1;
	for(int i = 1;i <= 19;++i) power[i] = power[i - 1] * 10;  
	
	read(L);read(R);read(X);read(Y);
	read(Q);
	
	node ansl=work(L - 1);
	
	while(Q--){
		read(K);
		LL l=L,r=R;
		bool judge = 0;
		while(l <= r){//二分大法好 
			
			LL mid = (l + r) >> 1;
			node cur = work(mid);
			
			if(cur.cnt - ansl.cnt == K) {
				put(cur.mans);putchar('\n');
				judge = 1;break;
			}
			else {
				if(cur.cnt - ansl.cnt < K) l = mid + 1;
				else r = mid - 1;
			}
		}
		
		if(! judge) printf("That's too bad!\n");
	}
	return 0;
}


你可能感兴趣的:(动规,-,数位DP,动规,DFS)