[kuangbin带你飞]专题十五 数位DP

A	CodeForces 55D	Beautiful numbers

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int MAXN = 25;
const int MOD = 2520;//1~9的lcm为2520
long long dp[MAXN][MOD][48];
int index[MOD + 10];//记录1~9的最小公倍数
int bit[MAXN];

int lcm(int a, int b)
{
	return a / __gcd(a, b)*b;
}

void init()
{
	int num = 0;
	for (int i = 1; i <= MOD; i++)
		if (MOD%i == 0)
			index[i] = num++;
}

long long dfs(int pos, int preSum, int preLcm, bool flag)
{
	if (pos == -1)
		return preSum%preLcm == 0;
	if (!flag && dp[pos][preSum][index[preLcm]] != -1)
		return dp[pos][preSum][index[preLcm]];
	long long ans = 0;
	int end = flag ? bit[pos] : 9;//上界
	for (int i = 0; i <= end; i++)
	{
		int nowSum = (preSum * 10 + i) % MOD;
		int nowLcm = preLcm;
		if (i)nowLcm = lcm(nowLcm, i);
		ans += dfs(pos - 1, nowSum, nowLcm, flag && i == end);
	}
	if (!flag)
		dp[pos][preSum][index[preLcm]] = ans;
	return ans;
}

long long calc(long long x)
{
	int pos = 0;
	while (x)
	{
		bit[pos++] = x % 10;
		x /= 10;
	}
	return dfs(pos - 1, 0, 1, 1);
}

int main()
{
	int T;
	long long l, r;
	init();
	memset(dp, -1, sizeof(dp));
	scanf("%d", &T);
	while (T--)
	{
		scanf("%lld%lld", &l, &r);
		printf("%lld\n", calc(r) - calc(l - 1));
	}
	return 0;
}





B	HDU 4352	XHXJ's LIS

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;

LL dp[20][1 << 10][11];
int k, bit[20];

int update(int x, int s)
{
	for (int i = x; i<10; i++)
	{
		if (s&(1 << i))
			return (s ^ (1 << i)) | (1 << x);
	}
	return s | (1 << x);
}

int getK(int s)
{
	int cnt = 0;
	while (s)
	{
		if (s & 1)cnt++;
		s >>= 1;
	}
	return cnt;
}

LL dfs(int pos, int s, bool e, bool z) //e为上界,z记录前面的是否都为0,1代表全为0
{
	if (!pos)
		return getK(s) == k;
	if (!e && dp[pos][s][k] != -1)
		return dp[pos][s][k];
	int digit = e ? bit[pos] : 9;
	LL ans = 0;
	for (int i = 0; i <= digit; i++)
	{
		ans += dfs(pos - 1, (z && (i == 0)) ? 0 : update(i, s), e&&i == digit, z && (i == 0));
	}
	if (!e)dp[pos][s][k] = ans;
	return ans;
}

LL solve(LL n)
{
	int len = 0;
	while (n)
	{
		bit[++len] = n % 10;
		n /= 10;
	}
	return dfs(len, 0, 1, 1);
}

int main()
{
	int cas = 1, t;
	scanf("%d", &t);
	memset(dp, -1, sizeof dp);
	while (t--)
	{
		LL a, b;
		cin >> a >> b >> k;
		printf("Case #%d: %I64d\n", cas++, solve(b) - solve(a - 1));
	}
	return 0;
}





HDU 2089	不要62

#include<iostream>
#include<cstdio>
using namespace std;

int dp[10][10];
int d[10];

void init()
{
	dp[0][0] = 1;
	for (int i = 1; i <= 7; ++i)
		for (int j = 0; j <= 9; ++j)
			for (int k = 0; k <= 9; ++k)
				if (j != 4 && !(j == 6 && k == 2))
					dp[i][j] += dp[i - 1][k];
}

int solve(int n)
{
	int ans = 0;
	int len = 0;
	while (n)
	{
		++len;
		d[len] = n % 10;
		n /= 10;
	}
	d[len + 1] = 0;
	for (int i = len; i >= 1; --i)
	{
		for (int j = 0; j < d[i]; ++j)
		{
			if (d[i + 1] != 6 || j != 2)
				ans += dp[i][j];
		}
		if (d[i] == 4 || (d[i + 1] == 6 && d[i] == 2))
			break;
	}
	return ans;
}

int main()
{
	int m, n;
	init();
	while (scanf("%d%d", &m, &n) == 2)
	{
		if (n == 0 && m == 0) break;
		printf("%d\n", solve(n + 1) - solve(m));
	}
	return 0;
}





D	HDU 3555	Bomb

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef __int64 LL;
LL dp[27][3];
int c[27];
//dp[i][j]:长度为i的数的第j种状态
//dp[i][0]:长度为i但是不包含49的方案数
//dp[i][1]:长度为i且不含49但是以9开头的数字的方案数
//dp[i][2]:长度为i且包含49的方案数
void init()
{
	memset(dp, 0, sizeof(dp));
	dp[0][0] = 1;
	for (int i = 1; i <= 20; i++)
	{
		dp[i][0] = dp[i - 1][0] * 10 - dp[i - 1][1];//所有情况减去开头为4的情况
		dp[i][1] = dp[i - 1][0] * 1;//这个随便
		dp[i][2] = dp[i - 1][2] * 10 + dp[i - 1][1];//已经有的随便,有9的就加上4就行
	}
}

int cal(LL n)
{
	int k = 0;
	memset(c, 0, sizeof(c));
	while (n)
	{
		c[++k] = n % 10;
		n /= 10;
	}
	c[k + 1] = 0;
	return k;
}

void solve(int len, LL n)
{
	int flag = 0;                   //标记是否出现过49
	LL ans = 0;
	for (int i = len; i >= 1; i--)
	{
		ans += c[i] * dp[i - 1][2];       //为什么乘以c[i]因为可以有0~i-1个也就是i个
		if (flag)                    //如果之前出现过49那么直接乘以没有出现49的情况,因为初始时候已经乘以了有49的情况
			ans += c[i] * dp[i - 1][0];
		else if (c[i] > 4)           //这一位前面没有挨着49,但c[i]比4大,那么当这一位填4的时候,要加上dp[i-1][1]
			ans += dp[i - 1][1];
		if (c[i + 1] == 4 && c[i] == 9)
			flag = 1;
	}
	printf("%I64d\n", ans);
}

int main()
{
	int t;
	LL n;
	init();
	scanf("%d", &t);
	while (t--)
	{
		scanf("%I64d", &n);
		int len = cal(n + 1);
		solve(len, n);
	}
	return 0;
}





E	POJ 3252	Round Numbers

#include<cstdio>
#include<cstring>

int dp[50][50][50], digit[50], s, t;

//dfs间接枚举所有情况
int dfs(int pos, int zero, int one, int lead, int limit)//limit判断上一位数字是否达到上界,lead有没有1出现
{
	if (!pos)return zero >= one;
	if (!limit&&dp[pos][zero][one] != -1)
		return dp[pos][zero][one];
	int up = limit ? digit[pos] : 1, ans = 0;
	for (int i = 0; i <= up; i++)
		ans += dfs(pos - 1, lead ? zero + (i == 0) : 0, one + (i == 1), lead || i == 1, limit&&i == up);
	return  limit ? ans : dp[pos][zero][one] = ans;
}

int cal(int n)
{
	int len = 0;
	while (n)    digit[++len] = n % 2, n >>= 1;
	return dfs(len, 0, 0, 0, 1);
}

int main()
{
	memset(dp, -1, sizeof(dp));
	scanf("%d%d", &s, &t);
	printf("%d\n", cal(t) - cal(s - 1));
}





F	HDU 3709	Balanced Number

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;

long long dp[30][30][3000];
int num[30];

long long dfs(int pos, int center, int sum, bool flag)
{
	if (pos == 0) return sum == 0;
	if (sum<0) return 0;
	if (!flag&&dp[pos][center][sum] != -1) return dp[pos][center][sum];
	long long ans = 0;
	int maxn = flag ? num[pos] : 9;
	for (int i = 0; i <= maxn; i++)
	{
		ans += dfs(pos - 1, center, sum + i*(pos - center), i == maxn&&flag);
	}
	if (!flag) dp[pos][center][sum] = ans;
	return ans;
}

long long cal(long long x)
{
	long long ans = 0;
	int pos = 0;
	while (x)
	{
		num[++pos] = x % 10;
		x /= 10;
	}
	for (int i = 1; i <= pos; i++)
		ans += dfs(pos, i, 0, 1);
	return ans - pos + 2;
}
int main()
{
	int t;
	long long n, m;
	memset(dp, -1, sizeof(dp));
	scanf("%d", &t);
	while (t--)
	{
		scanf("%I64d%I64d", &n, &m);
		printf("%I64d\n", cal(m) - cal(n - 1));
	}
	return 0;
}





G	HDU 3652 B - number

#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 13
using namespace std;

int dp[20][15][4];
int num[20];

int dfs(int pos, int mo, int status, bool limit)
{
	if (!pos)  return status == 2 && mo == 0;
	if (!limit&&dp[pos][mo][status] != 0) return dp[pos][mo][status];

	int end = limit ? num[pos] : 9;
	int sum = 0;
	for (int i = 0; i <= end; i++)
	{
		int a = mo;
		int flag = status;
		if (flag == 0 && i == 1) flag = 1;
		if (flag == 1 && i == 3) flag = 2;
		if (flag == 1 && i != 1 && i != 3) flag = 0;
		sum += dfs(pos - 1, (a * 10 + i) % mod, flag, limit&&i == end);
	}
	return limit ? sum : dp[pos][mo][status] = sum;
}

int cal(int n)
{
	int pos = 1;
	memset(dp, 0, sizeof dp);
	while (n>0)
	{
		num[pos++] = n % 10;
		n /= 10;
	}
	return dfs(pos - 1, 0, 0, true);
}

int main()
{
	int n;
	while (~scanf("%d", &n))
	{
		printf("%d\n", cal(n));
	}
	return 0;
}





H	HDU 4734	F(x)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int digit[20], dp[20][10000], t, a, b, ans, cas = 1;//dp[i][j]表示一共i位的数其中小于j的个数

int dfs(int pos, int st, bool limit)
{
	if (pos == 0)  return st >= 0;
	if (!limit&&dp[pos][st] != -1) return dp[pos][st];
	int end = limit ? digit[pos] : 9, ans = 0;
	for (int i = 0; i <= end; i++)
		ans += dfs(pos - 1, st - i*(1 << (pos - 1)), limit&&i == end);
	if (!limit)  dp[pos][st] = ans;
	return ans;
}

int f(int x)
{
	int sum = 0, cnt = 0;
	while (x)    sum += (x % 10)*(1 << (cnt++)), x /= 10;
	return sum;
}

int cal(int a, int b)
{
	int cnt = 0;
	while (b)    digit[++cnt] = b % 10, b /= 10;
	return dfs(cnt, f(a), 1);
}

int main()
{
	scanf("%d", &t), memset(dp, -1, sizeof(dp));
	while (t--)  scanf("%d%d", &a, &b), printf("Case #%d: %d\n", cas++, cal(a, b));
}





I	HDU 4507	吉哥系列故事――恨7不成妻

/*总状态转移思路
(1) ans.cnt += tmp.cnt
(2) ans.s += tmp.s + [ i*10^p ]*tmp.cnt
(3) ans.ss += tmp.ss + 2*(i*10^p)*tmp.s + [(i*10^p)^2]*tmp.cnt(平方和公式)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstring>
using namespace std;
typedef long long ll;

const int N = 20;
const int mod = 1e9 + 7;

struct Node
{
	ll cnt, s, ss;                        //与 7 无关的数的个数,与 7 无关的数的和,与 7 无关的数的平方和
	Node() { cnt = -1; s = 0, ss = 0; } //默认的构造函数,初始化cnt顺便标记是否访问过
}dp[N + 2][10][10];


ll p[N + 2], dig[N + 2];                 //保存10的次方 PS:记得取模,保存数字的每一个数位上的数

void init()
{
	p[1] = 1;                           //因为我的数字的个位用dig[1]表示,所以我用p[1]记录10的0次方
	for (int i = 2; i < N; ++i) p[i] = (p[i - 1] * 10) % mod;
}

inline ll EX(ll x)                      //求平方
{
	x %= mod;
	return (x*x) % mod;
}

Node dfs(int len, int r1, int r2, bool up)
{
	if (len == 0)
	{
		Node tmp;
		tmp.cnt = (r1&&r2);             //数位和不是7的倍数且数字不是7的倍数,因为这里已经完成了一组排列,只要判断这里的就行了所以&&
		tmp.s = tmp.ss = 0;
		return tmp;
	}
	if (!up&&dp[len][r1][r2].cnt != -1)
		return dp[len][r1][r2];
	int n = up ? dig[len] : 9;
	Node ans; ans.cnt = 0;
	for (int i = 0; i <= n; ++i)
	{
		if (i == 7) continue;
		Node tmp = dfs(len - 1, (i + r1) % 7, (i + r2 * 10) % 7, up&&i == n);               //得到子状态
		ans.cnt = (ans.cnt + tmp.cnt) % mod;                                                //更新个数
		ans.s = (ans.s + (tmp.s + (i*p[len]) % mod*tmp.cnt%mod) % mod) % mod;               //更新数字
		ans.ss = (ans.ss + (tmp.ss + ((2LL * i*p[len] % mod)*tmp.s) % mod) % mod) % mod;    //式子太长分开写
		ans.ss = (ans.ss + (EX(i*p[len])*tmp.cnt) % mod) % mod;
	}
	if (!up) dp[len][r1][r2] = ans;
	return ans;
}

ll cal(ll x)
{
	int len = 0;
	while (x)
	{
		dig[++len] = x % 10;
		x /= 10;
	}
	return dfs(len, 0, 0, 1).ss;
}

int main()
{
	init();
	int T;
	scanf("%d", &T);
	while (T--)
	{
		ll l, r;
		scanf("%lld %lld", &l, &r);
		printf("%lld\n", ((cal(r) - cal(l - 1)) % mod + mod) % mod);
	}
	return 0;
}





J	SPOJ BALNUM		Balanced Numbers

#include <string.h>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;

LL dp[20][60000];
int bit[20];

bool check(int s)
{
	int num[10];
	for (int i = 0; i<10; i++)
	{
		num[i] = s % 3;
		s /= 3;
	}
	for (int i = 0; i<10; i++)
	{
		if (num[i] != 0)
		{
			if (i % 2 == 0 && num[i] == 2)return false;//偶数数字出现了奇数次,舍去
			if (i % 2 == 1 && num[i] == 1)return false;//奇数数字出现了偶数次,舍去
		}
	}
	return true;
}

int getnews(int x, int s)
{
	int num[10];
	for (int i = 0; i<10; i++)
	{
		num[i] = s % 3;
		s /= 3;
	}
	if (num[x] == 0)
		num[x] = 1;
	else
		num[x] = 3 - num[x];
	int news = 0;
	for (int i = 9; i >= 0; i--)
	{
		news *= 3;
		news += num[i];
	}
	return news;
}

LL dfs(int pos, int s, int flag, int z)
{
	if (pos == -1)	return check(s);
	if (!flag&&dp[pos][s] != -1)	return dp[pos][s];
	LL ans = 0;
	int end = flag ? bit[pos] : 9;
	for (int i = 0; i <= end; i++)
		ans += dfs(pos - 1, (z&&i == 0) ? 0 : getnews(i, s), flag&&i == end, z&&i == 0);

	if (!flag)dp[pos][s] = ans;
	return ans;
}

LL solve(LL n)
{
	int len = 0;
	while (n)
	{
		bit[len++] = n % 10;
		n /= 10;
	}
	return dfs(len - 1, 0, 1, 1);
}

int main()
{
	int T;
	memset(dp, -1, sizeof(dp));
	LL a, b;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%lld%lld", &a, &b);
		printf("%lld\n", solve(b) - solve(a - 1));
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值