数位dp入门 kuangbin数位dp专题

花了一周学习了数位dp (顺便学习了一下记忆化搜索QAQ)

感觉还是学到了很多的

数位dp 其实就是对每一位的数进行枚举 若果没有限制的话 就将这个状态保存下来 留给以后用 这其实就是记忆化搜索

我们先从不要62开始讲起吧

不要62

 

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

Input

输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。 

Output

对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。 

Sample Input

1 100
0 0

Sample Output

80

这道题算是一道板子题了 将每一位前的数保存下来然后枚举后一位数 看是否为62 或者 枚举到4就 继续

#include<bits/stdc++.h>
#define ll long long
int a[20];
ll dp[20][20];
ll n, m;
using namespace std;

ll dfs(int pos, int pre, bool limit){
	if(pos == 0)//枚举到0位 结束
		return 1;
	if(!limit && dp[pos][pre] != -1)//如果无限制 直接调用之前的结果
		return dp[pos][pre];
	int up = limit? a[pos] : 9;
	ll cnt = 0;
	for(int i = 0; i <= up; i++){
		if((i == 2 && pre == 6) || i == 4)
			continue;
		cnt += dfs(pos - 1, i, i == a[pos] && limit);
	}
	if(!limit)//如果无限制 保存这个状态
		dp[pos][pre] = cnt;
	return cnt;	
}

ll solve(ll x){
	int sum = 0;
	while(x){
		a[++sum] = x % 10;
		x = x / 10; 
	}
	return dfs(sum, 0, 1);
}

int main(){
	memset(dp, -1, sizeof(dp));
	while(cin >> n >> m && n != 0){
		cout << solve(m) - solve(n - 1) << endl;
	}
	
} 

第二题

 Bomb

 

The counter-terrorists found a time bomb in the dust. But this time the terrorists improve on the time bomb. The number sequence of the time bomb counts from 1 to N. If the current number sequence includes the sub-sequence "49", the power of the blast would add one point. 
Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them? 

Input

The first line of input consists of an integer T (1 <= T <= 10000), indicating the number of test cases. For each test case, there will be an integer N (1 <= N <= 2^63-1) as the description. 

The input terminates by end of file marker. 

Output

For each test case, output an integer indicating the final points of the power.

Sample Input

3
1
50
500

Sample Output

0
1
15
这题其实62是一样的 上一题代码改一下就行了
#include<bits/stdc++.h>
#define ll long long
int a[20];
ll dp[20][20];
ll n, m;
using namespace std;

ll dfs(int pos, int pre, bool limit){
	if(pos == 0)
		return 1;
	if(!limit && dp[pos][pre] != -1)
		return dp[pos][pre];
	int up = limit? a[pos] : 9;
	ll cnt = 0;
	for(int i = 0; i <= up; i++){
		if((i == 9 && pre == 4))
			continue;
		cnt += dfs(pos - 1, i, i == a[pos] && limit);
	}
	if(!limit)
		dp[pos][pre] = cnt;
	return cnt;	
}

ll solve(ll x){
	int sum = 0;
	while(x){
		a[++sum] = x % 10;
		x = x / 10; 
	}
	return dfs(sum, 0, 1);
}

int main(){
	int t;
	memset(dp, -1, sizeof(dp));
	cin >> t;
	while(t--){
		cin >> n;
		cout << n - solve(n) + 1 << endl;
	}
	
} 

 B-number

 

A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string "13" and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task is to calculate how many wqb-numbers from 1 to n for a given integer n.

Input

Process till EOF. In each line, there is one positive integer n(1 <= n <= 1000000000).

Output

Print each answer in a single line.

Sample Input

13
100
200
1000

Sample Output

1
1
2
2

题意求一个数包含13且能被13整除

这一题和前面的题有点不一样 但我们可以增加一个条件就是是否能整除13 上一位 * 10 + i 然后%13 模拟除法

最后算是否为0

#include<bits/stdc++.h>
#define ll long long
int a[20];
ll dp[20][20][3][20];
ll n, m;
using namespace std;

ll dfs(int pos, int pre, bool limit, int r, bool have13){
	if(pos == 0)
		return !r && have13;
	if(!limit && dp[pos][pre][have13][r] != -1)
		return dp[pos][pre][have13][r];
	int up = limit? a[pos] : 9;
	ll cnt = 0;
	for(int i = 0; i <= up; i++){
		int nextr = (r * 10 + i) % 13;
		if(have13 || (i == 3 && pre == 1)){			
			cnt += dfs(pos - 1, i, i == up && limit, nextr, 1);
		}
		else{
			cnt += dfs(pos - 1, i, i == up && limit, nextr, 0);
		}	
	}
	if(!limit)
		dp[pos][pre][have13][r] = cnt;
	return cnt;	
}

ll solve(ll x){
	int sum = 0;
	while(x){
		a[++sum] = x % 10;
		x = x / 10; 
	}
	return dfs(sum, 0, 1, 0, 0);
}

int main(){
	int t;
	memset(dp, -1, sizeof(dp));
	while(cin >> n){
		cout << solve(n)<< endl;
	}
	
} 

F(x)

 

For a decimal number x with n digits (A nA n-1A n-2 ... A 2A 1), we define its weight as F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A 1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there between 0 and B, inclusive, whose weight is no more than F(A).

Input

The first line has a number T (T <= 10000) , indicating the number of test cases. 
For each test case, there are two numbers A and B (0 <= A,B < 10 9)

Output

For every case,you should output "Case #t: " at first, without quotes. The t is the case number starting from 1. Then output the answer.

Sample Input

3
0 100
1 10
5 100

Sample Output

Case #1: 1
Case #2: 2
Case #3: 13

题意 

算一个数的0到x的f(x)是否小于前一个数的f(x)

可以维护一个状态计算前一个的f(x)-后一个数的每一位是否大于0

#include<bits/stdc++.h>
#define ll long long
int a[20];
int b[20];
ll ti[20];
ll dp[20][20000];
ll n, m;
ll Sum = 0;
using namespace std;

ll dfs(int pos, bool limit, int sm){
	if(pos == 0)
		return sm >= 0;
	if(!limit && dp[pos][sm] != -1)
		return dp[pos][sm];
	int up = limit? a[pos] : 9;
	ll cnt = 0;
	for(int i = 0; i <= up; i++){
		int nextr = sm - (i * ti[pos]);
		if(nextr < 0)			
			continue;
		cnt += dfs(pos - 1, i == up && limit, nextr);	
	}
	if(!limit)
		dp[pos][sm] = cnt;
	return cnt;	
}

ll solve(ll x, ll y){
	int sum = 0;
	Sum = 0;
	while(x){
		a[++sum] = x % 10;
		x = x / 10; 	
	}
	int cnt = 0;
	while(y){
		b[++cnt] = y % 10;
		y = y / 10; 	
	}
	for(int i = 1; i <= cnt; i++)
		Sum += b[i] * ti[i];		
	return dfs(sum, 1, Sum);
}


int main(){
	int t;
	memset(dp, -1, sizeof(dp));
	ti[1] = 1;
	for(int i = 2 ; i <= 20 ; i++) ti[i] = ti[i-1] * 2;
	cin >> t;
	int tot = t;
	while(t--){
		cin >> n >> m;
		cout << "Case #" << tot - t << ": " << solve(m, n) << endl;
	}
	
} 

Round Numbers

 

The cows, as you know, have no fingers or thumbs and thus are unable to play Scissors, Paper, Stone' (also known as 'Rock, Paper, Scissors', 'Ro, Sham, Bo', and a host of other names) in order to make arbitrary decisions such as who gets to be milked first. They can't even flip a coin because it's so hard to toss using hooves.

They have thus resorted to "round number" matching. The first cow picks an integer less than two billion. The second cow does the same. If the numbers are both "round numbers", the first cow wins,
otherwise the second cow wins.

A positive integer N is said to be a "round number" if the binary representation of Nhas as many or more zeroes than it has ones. For example, the integer 9, when written in binary form, is 1001. 1001 has two zeroes and two ones; thus, 9 is a round number. The integer 26 is 11010 in binary; since it has two zeroes and three ones, it is not a round number.

Obviously, it takes cows a while to convert numbers to binary, so the winner takes a while to determine. Bessie wants to cheat and thinks she can do that if she knows how many "round numbers" are in a given range.

Help her by writing a program that tells how many round numbers appear in the inclusive range given by the input (1 ≤ Start < Finish ≤ 2,000,000,000).

Input

Line 1: Two space-separated integers, respectively Start and Finish.

Output

Line 1: A single integer that is the count of round numbers in the inclusive rangeStart.. Finish

Sample Input

2 12

Sample Output

6

题意 计算一个区间的数的二进制中0 > 1的数目 

显然全是0需要计数 然后维护两个状态 0和1的个数 

#include<iostream>
#include<cstring>
#define ll long long
ll dig[100];
ll dp[100][100][100];
ll a, b;
using namespace std;
ll dfs(ll pos, ll sum1, ll sum0, bool limit, bool is0){
	if(pos == 0){
		if(is0)
			return 1;
		if(sum0 >= sum1)
			return 1;
		return 0;
	}
	if(!is0 && !limit && dp[pos][sum1][sum0] != -1)
		return dp[pos][sum1][sum0];
	ll up = limit? dig[pos] : 1;
	ll cnt = 0;
	for(ll i = 0; i <= up; i++){
		if(is0){
			if(i)
				cnt += dfs(pos - 1, sum1 + 1, sum0, i == up && limit, 0);
			else
				cnt += dfs(pos - 1, sum1, sum0, i == up && limit, 1);
		}
		else{
			if(i)
				cnt += dfs(pos - 1, sum1 + 1, sum0, i == up && limit, 0);
			else
				cnt += dfs(pos - 1, sum1, sum0 + 1, i == up && limit, 0);
		} 
		
	}
	if(!limit && !is0)
		dp[pos][sum1][sum0] = cnt;
	return cnt;
}
ll solve(ll x){
	ll sum = 0;
	while(x){
		dig[++sum] = x & 1;
		x >>= 1;
	}
	return dfs(sum, 0, 0, 1, 1);
}
int main(){
	memset(dp, -1, sizeof(dp));
	cin >> a >> b;
	cout << solve(b) - solve(a - 1);
}  

Balanced Number

 

A balanced number is a non-negative integer that can be balanced if a pivot is placed at some digit. More specifically, imagine each digit as a box with weight indicated by the digit. When a pivot is placed at some digit of the number, the distance from a digit to the pivot is the offset between it and the pivot. Then the torques of left part and right part can be calculated. It is balanced if they are the same. A balanced number must be balanced with the pivot at some of its digits. For example, 4139 is a balanced number with pivot fixed at 3. The torqueses are 4*2 + 1*1 = 9 and 9*1 = 9, for left part and right part, respectively. It's your job 
to calculate the number of balanced numbers in a given range [x, y].

Input

The input contains multiple test cases. The first line is the total number of cases T (0 < T ≤ 30). For each case, there are two integers separated by a space in a line, x and y. (0 ≤ x ≤ y ≤ 10 18).

Output

For each case, print the number of balanced numbers in the range [x, y] in a line.

Sample Input

2
0 9
7604 24324

Sample Output

10
897

枚举数然后算左右的力矩*数 使左右相等

枚举的每次都有一个0

所以要去掉sum- 1个

不容易想到

#include<bits/stdc++.h>
#define ll long long
int a[20];
ll dp[20][20000][20];
ll n, m;
ll t;
using namespace std;

ll dfs(ll pos, ll cha, ll p, bool limit){
	if(pos == 0)
		return cha ? 0 : 1;
	if(!limit && dp[pos][cha][p] != -1)
		return dp[pos][cha][p];
	ll up = limit? a[pos] : 9;
	ll cnt = 0;
	for(ll i = 0; i <= up; i++){
		cnt += dfs(pos - 1, cha + (pos - p) * i, p, i == a[pos] && limit);
	}
	if(!limit)
		dp[pos][cha][p] = cnt;
	return cnt;
}

ll solve(ll x){
	ll sum = 0;
	while(x){
		a[++sum] = x % 10;
		x = x / 10;
	}
	ll ans = 0;
	for(int i = 1; i <= sum; i++)
        ans += dfs(sum, 0, i, 1);
	return ans - sum + 1;
}

int main(){
    cin >> t;
	memset(dp, -1, sizeof(dp));
	while(t--){
		cin >> n >> m;
	        cout << solve(m) - solve(n - 1) << endl;

	}

}

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

 

单身! 
  依然单身! 
  吉哥依然单身! 
  DS级码农吉哥依然单身! 
  所以,他生平最恨情人节,不管是214还是77,他都讨厌! 
   
  吉哥观察了214和77这两个数,发现: 
  2+1+4=7 
  7+7=7*2 
  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

这一题挺难的

需要手推下公式

直接上代码吧。。。

#include<bits/stdc++.h>
#define ll long long
const ll mod = 1e9 + 7; 
ll t;
ll x, y;
ll pw[22];
ll p[22]; 
using namespace std;

struct node{
	ll su, sq, cn;
	node() {su = 0; sq = 0; cn = -1;}
	node(ll a, ll b, ll c) : su(a), sq(b), cn(c) {}
}dp[22][22][10]; 

node dfs(ll pos, ll r, ll sumw, bool limit){
	if(pos == 0)
		return r && sumw ? node(0, 0, 1) : node(0, 0, 0);
	if(dp[pos][r][sumw].cn != -1 && !limit)
		return dp[pos][r][sumw];
	ll up = limit? pw[pos] : 9;
	node cnt, nw;
	cnt.cn = 0;
	for(ll i = 0; i <= up; i++){
		if(i == 7)
			continue;
		nw = dfs(pos - 1, (r * 10 + i) % 7, (sumw + i) % 7, limit && i == up);
		ll k = pow(10, pos - 1);
		ll a  = (i * k) % mod;
		ll aa = a * a % mod;
		cnt.su = (cnt.su + nw.cn * a % mod + nw.su) % mod;
		cnt.sq = (cnt.sq + aa * nw.cn % mod + 2 * a * nw.su % mod + nw.sq) % mod;
		cnt.cn = (cnt.cn + nw.cn + mod) % mod;
	} 
	if(!limit)
		dp[pos][r][sumw] = cnt;
	return cnt;
}

ll solve(ll x){
	ll sum = 0;
	while(x){
		pw[++sum] = x % 10;
		x /= 10;
	}
	return dfs(sum, 0, 0, 1).sq;
}

int main(){
	cin >> t;
	p[1] = 1;
	for(int i = 2; i <= 20; i++)
		p[i] = p[i - 1] * 10 % mod;
	while(t--){
		cin >> x >> y;
		ll ed = solve(y) - solve(x - 1);
		cout << (ed % mod + mod) % mod << endl;
	}	
} 

Beautiful numbers

 

Volodya 是一个古怪的男孩,他的品味与众不同。对他来说,一个正整数是 漂亮数 ,当且仅当它能够被自身的各非零数字整除。我们不必与之争辩,只需计算给定范围中有多少个漂亮数。

输入

输入的第一行包含了测试用例的数目 t (1 ≤ t ≤ 10)。接下来的 t 行,每行包含两个自然数 li 和 ri(1 ≤ li ≤ ri ≤ 9 ·1018)。

备注:请不要在 C++ 中使用 %lld 读写 64-位 整数。在这个评测系统中,倾向于使用 cin (也可以使用 %I64d)。

输出

输出应当包含 t 个数 — 问题的答案,每行一个数 — 表示给定范围 (从 li 到 ri 闭区间) 中的漂亮数有多少个。

示例

输入

1
1 9

输出

9

输入

1
12 15

输出

2

需要一些数学基础

#include<bits/stdc++.h>
#define ll long long
ll dig[50];
ll f[5000005];
ll dp[30][3000][50];
ll t;
ll a, b;

using namespace std;

ll lcm(ll x, ll y){
    return x /  __gcd(x, y) * y ;
}

ll dfs(int pos, ll lcms, ll r, bool limit){
    if(pos == 0)
        return r % lcms == 0;
    if(!limit && dp[pos][r][f[lcms]] != -1)
        return dp[pos][r][f[lcms]];
    ll up = limit ? dig[pos] : 9;
    ll cnt = 0;
    for(ll i = 0; i <= up; i++){
        if(i == 0)
            cnt += dfs(pos - 1, lcms, (r * 10 + i) % 2520, i == up && limit);
        else
            cnt += dfs(pos - 1, lcm(lcms, i), (r * 10 + i) % 2520, i == up && limit);
    }
    if(!limit)
        dp[pos][r][f[lcms]] = cnt;
    return cnt;
}

ll solve(ll x){
    int sum = 0;
    while(x){
        dig[++sum] = x % 10;
        x /= 10;
    }
    return dfs(sum, 1, 0, 1);
}

int main(){
    cin >> t;
    memset(dp, -1, sizeof(dp));
    memset(f, 0, sizeof(f));
    int tmp = 0;
    for(int i = 1; i <= 2520; i++){
        if(2520 % i == 0)
            f[i] = tmp++;
    }
    while(t--){
        cin >> a >> b;
        cout << solve(b) - solve(a - 1) << endl;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值