SDUT - 2609 A-Number and B-Number(二分+数位dp)

题目链接:点击查看

题目大意:规定 A 数组为所有十进制下含有 7 或者可以被 7 整除的数字,例如 A 数组中的前 10 个数为: {a[1]=7,a[2]=14,a[3]=17,a[4]=21,a[5]=27,a[6]=28,a[7]=35,a[8]=37,a[9]=42,a[10]=47},同时规定 B 数组为 A 数组的一个子集,其中不含有以 A 中元素作为下标的 A 数组,例如 B 数组中的前 10 个数为: {b[1]=7,b[2]=14,b[3]=17,b[4]=21,b[5]=27,b[6]=28,b[7]=37,b[8]=42,b[9]=47,b[10]=49},因为 7 是 A 数组中的元素,所以 B 中不能含有 A[ 7 ] ,即不能含有 35 这个元素

题目分析:首先给定一个数字 x,不难用 数位dp 求出 x 前面共有多少个 A 数组中的元素,不要忘记减去 1 ,代表答案为 0 时的贡献,现在需要求一下 B 数组与 A 数组的关系,因为 B 数组是 A 数组的一个子集,所以考虑先计算出 A 数组的大小为 F( x ) ,也就是说此时的 A 数组为 a[ 1 ] , a[ 2 ] ... a[ F( x ) ] ,此时 A 数组中最大的下标也就是 F( x ) 了,因为 B 数组中不能含有的数字与其在 A 数组红的下标有关系,所以再单独求一下 F( x ) 前面共有多少个 A 数组的元素,这些元素是不可以出现在 B 数组中的,换句话说,B 数组的表达式就是 F( x ) - F( F( x ) ) 了,打个表观察一下不难发现这个值具有单调性,所以可以直接二分答案然后判断

代码:
 

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
 
typedef long long LL;
 
typedef unsigned long long ull;
 
const int inf=0x3f3f3f3f;

const int N=1e5+100;

LL dp[70][10][2];//dp[pos][mod][have_7?]

int b[70];

LL dfs(int pos,int mod,bool flag,bool limit)
{
	if(pos==-1)
		return flag||mod==0;
	if(!limit&&dp[pos][mod][flag]!=-1)
		return dp[pos][mod][flag];
	int up=limit?b[pos]:9;
	LL ans=0;
	for(int i=0;i<=up;i++)
		ans+=dfs(pos-1,(mod*10+i)%7,flag||(i==7),limit&&i==up);
	if(!limit)
		dp[pos][mod][flag]=ans;
	return ans;
}

LL solve(LL num)
{
	int cnt=0;
	while(num)
	{
		b[cnt++]=num%10;
		num/=10;
	}
	return dfs(cnt-1,0,false,true)-1;
}
 
int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	memset(dp,-1,sizeof(dp));
	LL n;
	while(scanf("%lld",&n)!=EOF)
	{
		ull l=1,r=LLONG_MAX,ans;
		while(l<=r)
		{
			ull mid=l+r>>1;
			LL temp=solve(mid);
			if(temp-solve(temp)>=n)
			{
				ans=mid;
				r=mid-1;
			}
			else
				l=mid+1;
		}
		printf("%llu\n",ans);
	}








	return 0;
}

 

你可能感兴趣的:(二分,数位dp)