数位dp

八点钟搞到现在

实在搞不动了

吃饭去。。。

明天不能这样了。。。

十二点之后明显脑子转不动了。。。

各种写不出


今天搞了数位dp

看了好多神犇的博客。。。

在这里不一一列举了


一开始两个题用递推写

各种难懂

后来学会了记忆化搜索

腰不酸了腿不疼了


基本的题现在可以写的出了。。。


代码如有雷同。。

那是因为。。。

我拿来当模版了。。。


Ps:

今天在bnuoj上搞了两场比赛

方便做题和总结


A. Bomb

1000ms
1000ms
65536KB
64-bit integer IO format:  %I64d      Java class name:  Main
Font Size:   
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


  
  
Hint
From 1 to 500, the numbers that include the sub-sequence "49" are "49","149","249","349","449","490","491","492","493","494","495","496","497","498","499", so the answer is 15.
/*
学习数位dp
感觉就是细心点就好
加油
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
long long dp[25][3]; 
int bit[25];
void init()
{
	//dp[i][0]表示长度为i,包含49的个数
	//dp[i][1]表示长度为i,不包含49但开头为9的个数
	//dp[i][2]表示长度为i,不包含49的个数 
	memset(dp,0,sizeof(dp));
	dp[0][2]=1;
	for(int i=1;i<=20;i++)
	{
		dp[i][0]=dp[i-1][0]*10+1*dp[i-1][1];
		dp[i][1]=1*dp[i-1][2];
		dp[i][2]=dp[i-1][2]*10-1*dp[i-1][1];
	}
}
long long solve(long long n)
{
	int len=0;
	while(n)
	{
		bit[++len]=n%10;
		n/=10;
	}
	bit[len+1]=0;
	long long ans=0;
	int flag=0;//标记高位是否出现49 
	for(int i=len;i>0;i--)
	{
		ans+=dp[i-1][0]*bit[i];
		if(flag)//如果已经出现49,那么显然随意加 
		{
			ans+=dp[i-1][2]*bit[i];
		}
		if(!flag&&bit[i]>4)//如果没有出现49,但bit[i]>4,那么当这一位填4的时候,需要加上dp[i-1][1] 
		{
			ans+=dp[i-1][1];
		}
		if(bit[i]==9&&bit[i+1]==4)//出现49那么标记flag 
		{
			flag=1;
		}
	}
	return ans;
}
	
int main()
{
	init();
	int T;
	cin>>T;
	//scanf("%d",&T);
	while(T--)
	{
		long long n;
		cin>>n;
		//scanf("%I64d",&n);
		cout<<solve(n+1)<<endl;
		//printf("%I64d\n",solve(n+1));
	}
	return 0;
}

/*
记忆化搜索版
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
long long dp[20][3];
int bit[20]; 
//dp[pos][have]
//pos :当前位置 
//have: 0未出现49 1未出现49且pos+1位为4 2出现49 
long long dfs(int pos,int have,int doing)
{
	if(pos==-1)
	{
		if(have==2)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	if(!doing&&dp[pos][have]!=-1)
	{
		return dp[pos][have];
	}
	long long ans=0;
	int end=doing?bit[pos]:9;
	for(int i=0;i<=end;i++)
	{
		int nhave=have;
		if(have==1)
		{
			if(i!=4)
			{
				nhave=0;
			}
			if(i==9)
			{
				nhave=2;
			}
		}
		if(have==0&&i==4)
		{
			nhave=1;
		}
		ans+=dfs(pos-1,nhave,doing&&i==end);
	}
	if(!doing)
	{
		dp[pos][have]=ans;
	}
	return ans;
}
long long solve(long long n)
{
	int len=0;
	while(n)
	{
		bit[len++]=n%10;
		n/=10;
	}
	return dfs(len-1,0,1);
}
int main()
{
	memset(dp,-1,sizeof(dp));
	int T;
	cin>>T;
	while(T--)
	{
		long long n;
		cin>>n;
		cout<<solve(n)<<endl;
	}
	return 0;
}

B. B-number

1000ms
1000ms
32768KB
64-bit integer IO format:  %I64d      Java class name:  Main
Font Size:   
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
/*
记忆化搜索解决数位dp
感觉这种方法写起来简单一点。。。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib> 
using namespace std;
//dp[pos][pre][have]
int dp[10][13][13];
int bit[10];
//pos 当前位置
//pre 之前已经确定的部分 (余数) 
//have(0:没有13 1:没有13但pos+1位为1 0:含有13) 
//doing 是否有限制 
int dfs(int pos,int pre,int have,int doing)
{
	if(pos==-1)
	{
		if(have==2&&pre==0)//余数为0并且已经出现13 
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	//无限制 
	if(!doing&&dp[pos][pre][have]!=-1)
	{
		return dp[pos][pre][have];
	}
	int ans=0;
	int end=doing?bit[pos]:9;//限制
	for(int i=0;i<=end;i++)
	{
		int npre=(pre*10+i)%13;
		int nhave=have;
		if(have==0&&i==1)
		{
			nhave=1;
		}
		else if(have==1)
		{
			if(i!=1)
			{
				nhave=0;
			}
			if(i==3)
			{
				nhave=2;
			}
		}
		ans+=dfs(pos-1,npre,nhave,doing&&i==end);
	}
	if(!doing)
	{
		dp[pos][pre][have]=ans;
	}
	return ans;
}
			
int solve(int n)
{
	int len=0;
	while(n)
	{
		bit[len++]=n%10;
		n/=10;
	}
	return dfs(len-1,0,0,1);
}
int main()
{
	memset(dp,-1,sizeof(dp));
	int n;
	while(cin>>n)
	{
		cout<<solve(n)<<endl;
	}
	return 0;
}

C. Amount of Degrees

1000ms
1000ms
16384KB
64-bit integer IO format:  %lld      Java class name:  (Any)
Font Size:   
Create a code to determine the amount of integers, lying in the set [ X; Y] and being a sum of exactly  K different integer degrees of  B.
Example. Let  X=15,  Y=20,  K=2,  B=2. By this example 3 numbers are the sum of exactly two integer degrees of number 2:
17 = 2 4+2 0,
18 = 2 4+2 1,
20 = 2 4+2 2.

Input

The first line of input contains integers  X and  Y, separated with a space (1 ≤  X ≤  Y ≤ 2 31−1). The next two lines contain integers  K and  B (1 ≤  K ≤ 20; 2 ≤  B ≤ 10).

Output

Output should contain a single integer — the amount of integers, lying between  X and  Y, being a sum of exactly  Kdifferent integer degrees of  B.

Sample Input

15 20
2
2

Sample Output

3
/*
恰好等于K个互不相等的B的整数次幂之和。。
即这个数的B进制表示中恰好有K个1。。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
long long dp[35][25];
int bit[35];
int x,y,b,k;
long long dfs(int pos,int num,int doing)
{
	if(pos==-1)
	{
		if(num==k)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	if(num>k)
	{
		return 0;
	}
	if(!doing&&dp[pos][num]!=-1)
	{
		return dp[pos][num];
	}
	long long ans=0;
	int end=doing?min(bit[pos],1):1;
	for(int i=0;i<=end;i++)
	{
		int nnum=num;
		if(i)
		{
			nnum=num+1;
		}
		ans+=dfs(pos-1,nnum,doing&&i==bit[pos]);
	}
	if(!doing)
	{
		dp[pos][num]=ans;
	}
	return ans;
}
			
int solve(int n)
{
	int len=0;
	while(n)
	{
		bit[len++]=n%b;
		n/=b;
	}
	return dfs(len-1,0,1);
}
int main()
{
	memset(dp,-1,sizeof(dp));
	while(cin>>x>>y>>k>>b)
	{
		cout<<solve(y)-solve(x-1)<<endl;
	}
	return 0;
}

D. K-th Nya Number

1000ms
1000ms
65536KB
64-bit integer IO format:  %I64d      Java class name:  Main
Font Size:   
Arcueid likes nya number very much.
A nya number is the number which has exactly X fours and Y sevens(If X=2 and Y=3 , 172441277 and 47770142 are nya numbers.But 14777 is not a nya number ,because it has only 1 four).
Now, Arcueid wants to know the K-th nya number which is greater than P and not greater than Q.

Input

The first line contains a positive integer T (T<=100), indicates there are T test cases.
The second line contains 4 non-negative integers: P,Q,X and Y separated by spaces.
( 0<=X+Y<=20 , 0< P<=Q <2^63)
The third line contains an integer N(1<=N<=100).
Then here comes N queries.
Each of them contains an integer K_i (0<K_i <2^63).

Output

For each test case, display its case number and then print N lines.
For each query, output a line contains an integer number, representing the K_i-th nya number in (P,Q].
If there is no such number,please output "Nya!"(without the quotes).

Sample Input

1
38 400 1 1
10
1
2
3
4
5
6
7
8
9
10

Sample Output

Case #1:
47
74
147
174
247
274
347
374
Nya!
Nya!
/*
数位dp+二分
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
long long dp[25][25][25];
int bit[25];
int x,y;
long long p,q,base;
long long dfs(int pos,int a,int b,int doing)
{
	if(pos==-1)
	{
		if(a==x&&b==y)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	if(!doing&&dp[pos][a][b]!=-1)
	{
		return dp[pos][a][b];
	}
	long long ans=0;
	int end=doing?bit[pos]:9;
	int na,nb;
	for(int i=0;i<=end;i++)
	{
		na=a;
		nb=b;
		if(i==4)
		{
			na++;
		}
		if(i==7)
		{
			nb++;
		}
		ans+=dfs(pos-1,na,nb,doing&&i==end);
	}
	if(!doing)
	{
		dp[pos][a][b]=ans;
	}
	return ans;
}
		
long long solve(long long n)
{
	int len=0;
	while(n)
	{
		bit[len++]=n%10;
		n/=10;
	}
	return dfs(len-1,0,0,1);
}
long long binarysearch(long long k)
{
	long long left=p+1;
	long long right=q;
	long long mid;
	long long ret;
	long long ans=-1;
	while(left<right)
	{
		mid=(left+right)/2;
		ret=solve(mid);
		if(ret<k)
		{
			left=mid+1;
		}
		else
		{
			right=mid;
		}
	}
	return left;
}
int main()
{
	int T;
	cin>>T;
	for(int tt=1;tt<=T;tt++)
	{
		int n;
		cin>>p>>q>>x>>y;
		cin>>n;
		memset(dp,-1,sizeof(dp));
		long long A=solve(p);
		long long cnt=solve(q)-A;
		cout<<"Case #"<<tt<<":"<<endl;
		for(int i=1;i<=n;i++)
		{
			long long k;
			cin>>k;
			if(k>cnt)
			{
				cout<<"Nya!\n";
				continue;
			}
			else
			{
				cout<<binarysearch(k+A)<<endl;
			}
		}
	}
	return 0;
}
		 

E. Beautiful numbers

4000ms
4000ms
262144KB
64-bit integer IO format:  %I64d      Java class name:  (Any)
Font Size:   

Volodya is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits. We will not argue with this and just count the quantity of beautiful numbers in given ranges.

Input

The first line of the input contains the number of cases t (1 ≤ t ≤ 10). Each of the next t lines contains two natural numbersli and ri (1 ≤ li ≤ ri ≤ 9 · 1018).

Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preffered to use cin (also you may use%I64d).

Output

Output should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri, inclusively).

Sample Input

Input
1
1 9
Output
9
Input
1
12 15
Output
2
/*
/*
    a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits.
    问一个区间内[l,r]有多少个Beautiful数字
    范围9*10^18
    
    数位统计问题,构造状态也挺难的,我想不出,我的思维局限在用递推去初始化状态,而这里的状态定义也比较难
    跟pre的具体数字有关

    问了NotOnlySuccess的,豁然开朗  Orz
    
    一个数字要被它的所有非零位整除,即被他们的LCM整除,可以存已有数字的Mask,但更好的方法是存它们的LCM{digit[i]}
    int MOD = LCM{1,2,9} = 5 * 7 * 8 * 9 = 2520
    按照定义,数字x为Beautiful : 
    x % LCM{digit[xi]} = 0
    即 x % MOD % LCM{digit[xi]} = 0
    所以可以只需存x % MOD,范围缩小了
    而在逐位统计时,假设到了pre***(pre指前面的一段已知的数字,而*是任意变)
        ( preSum * 10^pos + next )  % MOD % LCM(preLcm , nextLcm)
    =  ( preSum * 10 ^ pos % MOD + next % MOD ) % LCM(preLcm , nextLcm)
    == 0
    而next,nextLcm是变量,上面的比较式的意义就是
    在已知pos , preSum , preLcm情况下有多少种(next,nextLcm)满足式子为0
    而这个就是一个重复子问题所在的地方了,需要记录下来,用记忆化搜索
    dfs(pos , preSum , preLcm , doing)
    加一个标记为doing表示目前是在计算给定数字的上限,还是没有上限,即***类型的
    这样就将初始化以及逐位统计写在一个dfs了,好神奇!!!
    
    还有一点,10以内的数字情况为2^3 , 3^2 , 5 , 7
    所以最小公倍数组合的情况只有4*3*2*2 = 48
    可以存起来,我看NotOnlySuccess的写法是
    for(int i = 1 ; i <= MOD ; i ++)
    {
        if(MOD % i == 0)
            index[i] = num++;
    }
    很棒!!

    所以复杂度大概为19*2520*48*10(状态数*决策数)

    我觉得这题状态的设计不能跟具体数字分开,否则会很难设计吧
    所以用记忆化搜索,存起来
    用具体数字去计算,重复的子问题跟pre关系比较密切
    有一个比较重要的切入点就是LCM,还有%MOD缩小范围,才能存储

    还有优化到只需%252的,更快
    不过我觉得%2520比较好理解
*/
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
const int mod=2520;
int index[2530];
int bit[20];
long long dp[20][mod][50];
int gcd(int a,int b)
{
	int r=a%b;
	while(r)
	{
		a=b;
		b=r;
		r=a%b;
	}
	return b;
}
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,int doing)
{
	if(pos==-1)
	{
		if(presum%prelcm==0)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	if(!doing&&dp[pos][presum][index[prelcm]]!=-1)
	{
		return dp[pos][presum][index[prelcm]];
	}
	long long ans=0;
	int end=doing?bit[pos]:9;
	for(int i=0;i<=end;i++)
	{
		int nsum=(presum*10+i)%mod;
		int nlcm=prelcm;
		if(i)
		{
			nlcm=lcm(nlcm,i);
		}
		ans+=dfs(pos-1,nsum,nlcm,doing&&i==end);
	}
	if(!doing)
	{
		dp[pos][presum][index[prelcm]]=ans;
	}
	return ans;
}
long long solve(long long n)
{
	int len=0;
	while(n)
	{
		bit[len++]=n%10;
		n/=10;
	}
	return dfs(len-1,0,1,1);
}
int main()
{
	init();
	memset(dp,-1,sizeof(dp));
	int T;
	cin>>T;
	while(T--)
	{
		long long left,right;
		cin>>left>>right;
		cout<<solve(right)-solve(left-1)<<endl;
	}
	return 0;
}

F. windy数

1000ms
1000ms
65536KB
64-bit integer IO format:  %lld      Java class name:  Main
Font Size:   

windy定义了一种windy数。
不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。
windy想知道,在A和B之间,包括A和B,总共有多少个windy数?

Input

包含两个整数,A B。
满足 1 <= A <= B <= 2000000000 。

Output

包含一个整数:闭区间[A,B]上windy数的个数。

Sample Input

1 10

Sample Output

9

/*
还不太理解。。。
沉淀一会儿吧。。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib> 
#include<cmath>
using namespace std;
int dp[15][10];
int bit[15];
void init()
{
	//dp[i][j]表示长度为i且最高位为j的windy数的个数
	memset(dp,0,sizeof(dp));
	for(int i=0;i<=9;i++)
	{
		dp[1][i]=1;
	}
	for(int i=2;i<=10;i++)
	{
		for(int j=0;j<=9;j++)
		{
			for(int k=0;k<=9;k++)
			{
				if(abs(j-k)>=2)
				{
					dp[i][j]+=dp[i-1][k];
				}
			}
		}
	}
}
int solve(int n)
{
	int len=0;
	while(n)
	{
		bit[++len]=n%10;
		n/=10;
	}
	bit[len+1]=0;
	int ans=0;
	for(int i=1;i<len;i++)//长度为len-1 
	{
		for(int j=1;j<=9;j++)//最高位不能是0 
		{
			ans+=dp[i][j];
		}
	}
	for(int i=1;i<bit[len];i++)//长度为len,最高位是bit[len]-1 
	{
		ans+=dp[len][i];
	}
	for(int i=len-1;i>0;i--)//长度为len,最高位为bit[len] 
	{
		for(int j=0;j<bit[i];j++)
		{
			if(abs(bit[i+1]-j)>=2)
			{
				ans+=dp[i][j];
			}
		}
		if(abs(bit[i]-bit[i+1])<2)
		{
			break;
		}
	}
	return ans;
}
		
		 
int main()
{
	init();
	int left;
	int right;
	while(cin>>left>>right)
	{
		cout<<solve(right+1)-solve(left)<<endl;
	}
	return 0;
}

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
int dp[25][15][2];
int bit[25]; 
//dp[pos][pre][have]
//pos当前位置
//pre之前已经确定的部分
//have 前面是否全为0 1:不是全为0 0:全为0 
int dfs(int pos,int pre,int have,int doing)
{
	if(pos==-1)
	{
		if(have)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	if(!doing&&dp[pos][pre][have]!=-1)
	{
		return dp[pos][pre][have];
	}
	int ans=0;
	int end=doing?bit[pos]:9;
	for(int i=0;i<=end;i++)
	{
		int nhave=have;
		if(have==0)
		{
			if(i==0)
			{
				nhave=0;
			}
			else
			{
				nhave=1;
			}
			ans+=dfs(pos-1,i,nhave,doing&&i==end);
		}
		else if(abs(i-pre)>=2)
		{
			nhave=1;
			ans+=dfs(pos-1,i,nhave,doing&&i==end);
		}
	//	ans+=dfs(pos-1,i,nhave,doing&&i==end);
	}
	if(!doing)
	{
		dp[pos][pre][have]=ans;
	}
	return ans;
}
int solve(int n)
{
	int len=0;
	while(n)
	{
		bit[len++]=n%10;
		n/=10;
	}
	return dfs(len-1,0,0,1);
}
int main()
{
	memset(dp,-1,sizeof(dp));
	int a,b;
	while(cin>>a>>b)
	{
		cout<<solve(b)-solve(a-1)<<endl;
	}
	return 0;
}

A. Jason的特殊爱好

1000ms
1000ms
32768KB
64-bit integer IO format:  %I64d      Java class name:  Main
Font Size:   

Jason很喜欢数字,特别是1这个数字,因为他觉得1有特殊的含义。为了让更多的人喜欢上1,他决定出一题关于1的水题(每个人都喜欢水题)。

Input

输入数据中有多组数据,每组数据输入为两个正数,a,b(1<=a,b<=10^18)。

Output

输出a到b之间的整数包含多少个1。

Sample Input

1 1000

Sample Output

301
/*
TT..
不会做。。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
long long p[20];
long long s[20];
long long dp[20][10];
int bit[20];
long long dfs(int pos,int pre,int doing)
{
	if(pos==-1)
	{
		if(pre==1)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	if(!doing&&dp[pos][pre]!=-1)
	{
		return dp[pos][pre];
	}
	long long ans=0;
	int end=doing?bit[pos]:9;
	for(int i=0;i<=end;i++)
	{
		ans+=dfs(pos-1,i,doing&&i==end);
	}
	if(pre==1)
	{
		if(!doing)
		{
			ans+=p[pos+1];
		}
		else
		{
			ans+=s[pos]+1;
		}
	}
	return doing?ans:dp[pos][pre]=ans;
}
long long solve(long long n)
{
	int len=0;
	long long x=n;
	while(n)
	{
		bit[len]=n%10;
		n/=10;
		s[len]=x%p[len+1];
		len++;
	}
	return dfs(len-1,0,1);
}
int main()
{
	memset(dp,-1,sizeof(dp));
	p[0]=1;
	for(int i=1;i<20;i++)
	{
		p[i]=p[i-1]*10;
	}
	long long a,b; 
	while(cin>>a>>b)
	{
		cout<<solve(b)-solve(a-1)<<endl;
	}
	return 0;
}

B. 不要62

1000ms
1000ms
32768KB
64-bit integer IO format:  %I64d      Java class name:  Main
Font Size:   
杭州人称那些傻乎乎粘嗒嗒的人为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
/*
基本数位dp
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
int dp[15][3];
int bit[15];
//dp[pos][have]
//dp[pos][0]表示没有62 
//dp[pos][1]表示没有62但最高位为6
//dp[pos][2]表示有62 
int dfs(int pos,int have,int doing)
{
	if(pos==-1)
	{
		if(have==2)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	if(!doing&&dp[pos][have]!=-1)
	{
		return dp[pos][have];
	}
	int ans=0;
	int end=doing?bit[pos]:9;
	for(int i=0;i<=end;i++)
	{
		int nhave=have;
		if(have==2||i==4||(have==1&&i==2))
		{
			nhave=2;
		}
		else if(i==6)
		{
			nhave=1;
		}
		else
		{
			nhave=0;
		}
		ans+=dfs(pos-1,nhave,doing&&i==end);
	}
	if(!doing)
	{
		dp[pos][have]=ans;
	}
	return ans;
}
int solve(int n)
{
	int len=0;
	while(n)
	{
		bit[len++]=n%10;
		n/=10;
	}
	return dfs(len-1,0,1);
}
int main()
{
	memset(dp,-1,sizeof(dp));
	int n,m;
	while(cin>>n>>m,n!=0&&m!=0)
	{
		cout<<m-n+1-(solve(m)-solve(n-1))<<endl;
	}
	return 0;
}

F. Balanced Number

5000ms
5000ms
65535KB
64-bit integer IO format:  %I64d      Java class name:  Main
Font Size:   
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
/*
    题目描述见另外一份
    平衡,即∑a[i]*(i-o) = 0   o为支点
    对于一个数,支点o是唯一的,所以不会有重复计数的情况(但有一种特殊的,就是0,00,000等都是一样的,会计算多次,
    最后减去即可)
    假设检查到pos处,对于上面的式子∑a[i]*(i-o) = 0,这里确定了支点为o
    之前的数其∑a[i]*(i-o)的结果为pre
    所以参数需要为pos , o , pre 

    当检查到pos=-1时,return pre == 0
    否则,看当前是计算所有情况还是具体情况(doing)
    如果是所有情况且dp值!=-1,直接return
    否则就枚举0到end
    
    而支点o需要在最外层枚举出来
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
long long dp[20][20][2005];
int bit[20];
long long dfs(int pos,int index,int pre,int doing)
{
	if(pos==-1)
	{
		if(pre==0)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	if(!doing&&dp[pos][index][pre]!=-1)
	{
		return dp[pos][index][pre];
	}
	long long ans=0;
	int end=doing?bit[pos]:9;
	for(int i=0;i<=end;i++)
	{
		int npre=pre;
		npre+=(pos-index)*i;
		ans+=dfs(pos-1,index,npre,doing&&i==end);
	}
	if(!doing)
	{
		dp[pos][index][pre]=ans;
	}
	return ans;
}
long long solve(long long n)
{
	int len=0;
	while(n)
	{
		bit[len++]=n%10;
		n/=10;
	}
	long long ans=0;
	for(int i=0;i<len;i++)//枚举支点 
	{
		ans+=dfs(len-1,i,0,1);
	}
	return ans-(len-1);
}
int main()
{
	memset(dp,-1,sizeof(dp));
	int T;
	cin>>T;
	while(T--)
	{
		long long left,right;
		cin>>left>>right;
		cout<<solve(right)-solve(left-1)<<endl;
	}
	return 0;
}

G. SNIBB

1000ms
1000ms
32768KB
64-bit integer IO format:  %I64d      Java class name:  Main
Font Size:   
  As we know, some numbers have interesting property. For example, any even number has the property that could be divided by 2. However, this is too simple. 
  One day our small HH finds some more interesting property of some numbers. He names it the “Special Numbers In Base B” (SNIBB). Small HH is very good at math, so he considers the numbers in Base B. In Base B, we could express any decimal numbers. Let’s define an expression which describe a number’s “SNIBB value”.(Note that all the “SNIBB value” is in Base 10)
  

    Here N is a non-negative integer; B is the value of Base.
  For example, the “SNIBB value” of “1023” in Base “2” is exactly:10
(As we know (1111111111)2=(1023)(10))
  Now it is not so difficult to calculate the “SNIBB value” of the given N and B.
But small HH thinks that must be tedious if we just calculate it. So small HH give us some challenge. He would like to tell you B, the “SNIBB value” of N , and he wants you to do two kinds of operation:
1.  What is the number of numbers (whose “SNIBB value” is exactly M) in the range [A,B];
2.  What it the k-th number whose “SNIBB value” is exactly M in the range [A,B]; (note that the first one is 1-th but not 0-th)

Here M is given.

Input

  There are no more than 30 cases.
  For each case, there is one integer Q,which indicates the mode of operation;
  If Q=1 then follows four integers X,Y,B,M, indicating the number is between X and Y, the value of base and the “SNIBB value”.
(0<=X,Y<=2000000000,2<=B<=64,0<=M<=300)
  If Q=2 then follows five integers X,Y,B,M,K, the first four integer has the same meaning as above, K indicates small HH want to know the k-th number whose “SNIBB value” is exactly M.
(1<=K<=1000000000)

Output

  Output contains two lines for each cases.
  The first line is the case number, the format is exactly “Case x:”, here x stands for the case index (start from 1.).
  Then follows the answer.
  If Q=2 and there is no such number in the range, just output “Could not find the Number!” (without quote!) in a single line.

Sample Input

1 0 10 10 3
2 0 10 10 1 2
1 0 10 2 1

Sample Output

Case 1:
1
Case 2:
10
Case 3:
4

  
  
Hint
In case 1, the number in the range [0,10] whose “SNIBB value” is exactly 3 is 3(in Base 10); In case 2, the numbers in the range [0,10] whose “SNIBB value” is exactly 1 are 1 and 10; Of course the 2-th number is 10. In case 3, the number in the range [0,10] whose “SNIBB value” is exactly 1 is 1,10,100,1000(in Base 2);
/*
目:将一个数转化成B进制后,他的val表示的是各位上的数字和。详见题目描述。。。。
http://acm.hdu.edu.cn/showproblem.php?pid=3271
首先还是预处理,dp[i][j]表示转化成B进制后,长度为i的数中,数字和为j的数字有多少个,感觉越来越像数位DP。。。
对于询问1:压根就是数位DP,从高位开始枚举,记录之前已经出现的位数和,然后枚举当前位。注意区间的开闭问题,边界处理好
对于询问2:首先通过询问1得出的数目,判断是否存在第K大,然后就是二分答案,判断[l,mid]中和为m的数有多少个。
*/
#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 30
#define inf 1<<29
#define MOD 2007
#define LL long long
using namespace std;
int dp[32][305];
//转换成B进制后,长度为i的数中各位和为j的个数
void Init(int b,int m){
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    for(int i=1;i<32;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k<b&&k+j<=m;k++)
                dp[i][j+k]+=dp[i-1][j];
}
//统计[0,n]中转换成b进制,和为m的个数
int clac(int n,int b,int m){
    int bit[35],len=0,t=n;
    while(t){
        bit[++len]=t%b;
        t/=b;
    }
    int sum=0,tot=0;
    for(int i=len;i;i--){
        for(int j=0;j<bit[i]&&m-tot-j>=0;j++)
            sum+=dp[i-1][m-tot-j];
        tot+=bit[i];
        if(tot>m) break;
    }
    //本身的和就是m,注意别落下
    if(tot==m) sum++;
    return sum;
}
int main(){
    int cas=0,kind,x,y,b,m,k;
    while(scanf("%d%d%d%d%d",&kind,&x,&y,&b,&m)!=EOF){
        Init(b,m);
        if(x>y) swap(x,y);
        printf("Case %d:\n",++cas);
        int cnt=clac(y,b,m)-clac(x-1,b,m);
        if(kind==1){
            printf("%d\n",cnt);
            continue;
        }
        scanf("%d",&k);
        if(k>cnt){
            puts("Could not find the Number!");
            continue;
        }
        int low=x,high=y,mid;
        while(low<high){
            //二分答案,判断在[l,mid]中和为m的个数
            mid=(int)((((LL)low+(LL)high))/2);
            int now=clac(mid,b,m)-clac(x-1,b,m);
            if(now<k)
                low=mid+1;
            else
                high=mid;
        }
        printf("%d\n",low);
    }
    return 0;
}

感觉基础一点的数位dp无非就是分析状态或者再加一个二分

难一点的自己也不会。。。


额。。

今天先到这儿吧。。

吃饭。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值