数位DP7题

目录 

[WOJ1516]Amount of degrees

[WOJ1120]数字计数

[WOJ1505]美丽数

[WOJ1132]self同类分布

[WOJ1218]odd-even number

[WOJ1517]Sorted bit sequence

[WOJ2755] [CQOI2016]手机号码


[WOJ1516]Amount of degrees

转化为对应进制, 即求改进制下L--R直接有多少个x, 满足x的B进制有K个1

#include<bits/stdc++.h>
using namespace std;
int L, R, K, B, a[50];
int f[50][20];
int dfs(int u,int cnt,int limit,int zero){
	if(u == 0) return (cnt == K);
	if(f[u][cnt] != -1 && !limit && !zero) return f[u][cnt];
	int ans = 0, to = limit ? a[u] : B-1;
	for(int i=0;i<=min(to, 1);i++) ans += dfs(u-1, cnt+i, limit && i==to, zero && i==0);
	if(!limit && !zero) f[u][cnt] = ans;
	return ans;
}
int Solve(int x){
	int num = 0; memset(f, -1, sizeof(f));
	while(x){a[++num] = x % B; x /= B;}
	return dfs(num, 0, 1, 1);
}
int main(){
	scanf("%d%d%d%d",&L,&R,&K,&B);
	printf("%d",Solve(R) - Solve(L-1));
}

[WOJ1120]数字计数

f[u][cnt]=\sum _{i=0}^{up}f[u-1][cnt+(i==digit)]

记忆化搜索就完了, 注意前导0的影响

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL L, R, ans[12], Need; int a[15];
LL f[10][15][15];
LL dfs(int u,LL sum,int limit,int zero){
	if(u == 0) return sum;
	if(!limit && !zero && f[Need][u][sum]!=-1) return f[Need][u][sum];
	int up = limit ? a[u] : 9; LL ans = 0;
	for(int i=0; i<=up; i++){
		if(i==0 && zero) ans += dfs(u-1, sum, limit && (i==up), zero && (i==0));
		else ans += dfs(u-1, sum + (Need==i), limit && (i==up), zero && (i==0));
	} if(!limit && !zero) f[Need][u][sum] = ans;
	return ans;
}
LL Solve(LL x){
	int num = 0;
	while(x){ a[++num] = x%10; x /= 10;}
	return dfs(num, 0, 1, 1);
}
int main(){
	memset(f, -1, sizeof(f));
	scanf("%lld%lld",&L,&R);
	for(int i=0;i<=9;i++){ Need = i;
		cout<< Solve(R) - Solve(L-1) << " ";
	} return 0;
}

[WOJ1505]美丽数

因为lcm(1--9) = 2520, 所以我们将每个数%2520是什么记下来就可以了

另外还要判断每个数出现没有, 状压就可以了, 取模有个优化, 模252就可以了(玄学, 证明不会)

f[u][Mod][state] =\sum _{i=1}^{up} f[u-1][Mod*10+i][state|(1<<(i-1))]

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL L, R, f[20][252][512]; int a[20];
int check(int Mod,int state){
	for(int i=1;i<=9;i++){
		if(state & (1<<(i-1))){
			if(Mod % i) return 0;
		}
	} return 1;
}
LL dfs(int u,int Mod,int state,int limit){
	if(u == 0) return check(Mod, state);
	Mod %= 252;
	if(!limit && f[u][Mod][state]!=-1) return f[u][Mod][state];
	int up = limit ? a[u] : 9; LL ans = 0;
	for(int i=0;i<=up;i++){
		if(i == 0) ans += dfs(u-1, Mod*10, state, limit && (i==up));
		else ans += dfs(u-1, Mod*10 + i, state|(1<<(i-1)), limit && (i==up));
	} 
	if(!limit) f[u][Mod][state] = ans;
	return ans;
}
LL calc(LL x){
	int num = 0;
	while(x){a[++num] = x%10; x /= 10;}
	return dfs(num, 0, 0, 1);
}
int main(){
	memset(f, -1, sizeof(f));
	while(~scanf("%lld%lld",&L,&R)){
		printf("%lld\n",calc(R) - calc(L-1));
	} return 0;
}

[WOJ1132]self同类分布

很明显可以想到枚举数字和

f[u][sum][Mod]=\sum_{i=1}^{up} f[u-1][sum+i][Mod*10+i]

f[0][sum=tot][Mod=0]=1

枚举tot, Mod对tot取模

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL L, R; int Mod, a[20];
LL f[20][200][200];
LL dfs(int u,int res,int sum,int limit){
	res %= Mod;
	if(u == 0) return (!res && sum == Mod);
	if(sum > Mod) return 0;
	if(!limit && f[u][res][sum]!=-1) return f[u][res][sum]; 
	int up = limit ? a[u] : 9; LL ans = 0;
	for(int i=0;i<=up;i++){
		ans += dfs(u-1, res*10 + i, sum + i, limit&(i==up));
	} if(!limit) f[u][res][sum] = ans; return ans;
}
LL Solve(LL x){
	int num = 0;
	while(x){a[++num] = x % 10; x /= 10;}
	LL ans = 0;
	for(int i=1;i<=num*9;i++){
		memset(f, -1, sizeof(f));
		Mod = i; ans += dfs(num, 0, 0, 1);
	} return ans;
}
int main(){
	scanf("%lld%lld",&L,&R);
	printf("%lld",Solve(R) - Solve(L-1));
}

[WOJ1218]odd-even number

发现只需记录上一位是奇数还是偶数, 长度是奇数还是偶数, 前导0特殊处理, 记忆化搜索时讨论一下就可以了

#include<bits/stdc++.h>
#define LL long long  
using namespace std;
LL L, R; int T, a[20]; LL f[20][2][2];
LL dfs(int u,int len,int val,int limit,int zero){
	if(u == 0) return (len ^ val);
	if(!limit && !zero && f[u][len][val] != -1) return f[u][len][val];
	int up = limit ? a[u] : 9; LL ans = 0;
	for(int i=0;i<=up;i++){
		if(zero) ans += dfs(u-1, 1, i & 1, limit & (i==up), zero & (i==0));
		else{
			if(i & 1){
				if(len ^ val) ans += dfs(u-1, 1, 1, limit & (i==up), zero & (i==0));
				if(len == 1 && val == 1) ans += dfs(u-1, 0, 1, limit & (i==up), zero & (i==0)); 
			}
			else{
				if(len == 0) ans += dfs(u-1, 1, 0, limit & (i==up), zero & (i==0));
				if(len == 1 && val == 0) ans += dfs(u-1, 0, 0, limit & (i==up), zero & (i==0));
			}
		}
	} if(!limit && !zero) f[u][len][val] = ans;
	return ans;
}
LL Solve(LL x){
	int num = 0;
	while(x){ a[++num] = x%10; x /= 10;}
	return dfs(num, 1, 0, 1, 1);
}
int main(){
	scanf("%d",&T);
	memset(f, -1, sizeof(f));
	for(int d=1; d<=T; d++){
		scanf("%lld%lld",&L,&R);
		cout<<"Case #"<<d<<": "; 
		cout<< Solve(R) - Solve(L-1) << '\n';
	} return 0;
}

[WOJ1517]Sorted bit sequence

挺有意思的一道题, 一般数位DP求k大都是二分答案

首先1的个数可以二分, 当我们确定1的个数后, 相当于在某个特定的1的个数中找原数的第k大, 又可以二分

于是两次数位DP, 负数按题目要求变换就可以了

#include<bits/stdc++.h>
#define inf ((1ll<<32)-1)
typedef long long LL;
using namespace std;
LL L, R, k, Up, a[35], f[35][35];
LL dp1(LL u,LL sum,LL limit){
	if(u == 0) return (sum <= Up);
	if(!limit && f[u][sum]!=-1) return f[u][sum];
	LL up = limit ? a[u] : 1, ans = 0;
	for(LL i=0;i<=up;i++){
		ans += dp1(u-1, sum+i, limit&&(i==up));
	} if(!limit) f[u][sum] = ans;
	return ans; 
}
LL calc(LL x){
	if(x < 0) return 0;
	LL num = 0;
	while(x){ 
	a[++num] = x % 2; 
	x /= 2;}
	return dp1(num, 0, 1);
}
LL dp2(LL u,LL sum,LL limit){
	if(u == 0){
		return (sum == Up);
	}
	if(!limit && f[u][sum] != -1) return f[u][sum];
	LL up = limit ? a[u] : 1, ans = 0;
	for(LL i=0;i<=up;i++){
		ans += dp2(u-1, sum+i, limit&&(i==up));
	} if(!limit) f[u][sum] = ans;
	return ans;
}
LL Solve(LL x){
	if(x < 0) return 0;
	LL num = 0; memset(f, -1, sizeof(f));
	while(x){ a[++num] = x % 2; x /= 2;}
	return dp2(num, 0, 1);
}
int main(){
	scanf("%lld%lld%lld",&L,&R,&k); int flag = 0;
	if(L<0) L = -L, R = -R, L--, R--, L = inf-L, R = inf-R, flag = 1;
	LL l = 0, r = 31;
	while(l<r){
		memset(f, -1, sizeof(f));
		LL mid = (l+r) >> 1; Up = mid;
		if(calc(R) - calc(L-1) > k) r = mid;
		else l = mid+1;
	}
	memset(f, -1, sizeof(f));
	Up = l-1; k -= (calc(R) - calc(L-1));
	Up = l; k += Solve(L-1); l = L, r = R;
	while(l<r){
		LL mid = (l+r) >> 1;
		if(Solve(mid) >= k) r = mid;
		else l = mid+1;
	} 
	if(!flag) cout<<l; else cout<<-(inf-l)-1;
	return 0;
}

[WOJ2755] [CQOI2016]手机号码

记录上一次和上上次的数, 4, 8 有无出现, 然后就是模板了

#include<bits/stdc++.h>
#define LL long long
LL L, R; int a[15];
LL f[20][2][15][15][2][2];
LL dfs(int u, int already, int las1, int las2, int is4, int is8, int limit){
	if(is4 && is8) return 0;
	if(u == 0) return already; 
	LL &res = f[u][already][las1][las2][is4][is8];
	if(!limit && res != -1) return res;
	int up = limit ? a[u] : 9; LL ans = 0;
	for(int i=0; i<=up; i++){
		ans += dfs(u-1, already||(i==las1 && i==las2), i, las1, is4||(i==4), is8||(i==8), limit&&(i==up));
	} if(!limit) res = ans;
	return ans;
}
LL Solve(LL x){
	if(x < 1e10) return 0;
	memset(f, -1, sizeof(f)); int num = 0;
	while(x){ a[++num] = x % 10, x /= 10;}
	LL ans = 0;
	for(int i=1;i<=a[num];i++)
		ans += dfs(num-1, 0, i, 0, (i==4), (i==8), i == a[num]);
	return ans;
} 
int main(){
	scanf("%lld%lld",&L,&R);
	printf("%lld", Solve(R) - Solve(L-1));
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值