NOIP模拟赛20191017 T3 number【二分+数位DP】

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20181220_xiang_m_y/article/details/102690490

题目描述:

给定整数 m,k,求出正整数 n 使得 n+1,n+2,…,2n 中恰好有 m 个数 在二进制下恰好有 k 个 1。有多组数据。
输入:
第一行一个整数 t 表示数据组数。接下来 t 行每行两个整数 m,k。
输出:
每组数据输出一行两个整数,分别表示满足条件的n的最小值和最大值(无穷多此行输出一个数-1)。保证 10^18 以内存在满足条件的 n。
t<=2000,0<=m<=10^18,1<=k<=64。

题目分析:

S ( i ) S(i) 表示 i i 的二进制表示中1的数量,设 f [ n ] [ k ] f[n][k] 1 1 n n S ( i ) = k S(i)=k 的数量。
因为 2 n = n < < 1 2n=n<<1 ,所以有 f [ 2 n ] [ k ] = f [ n ] [ k ] + f [ n ] [ k 1 ] [ k 1 = = S ( n ) ] f[2n][k]=f[n][k]+f[n][k-1]-[k-1==S(n)] (分 2 n 2n 的末尾为0或1讨论)
题目即求满足 f [ 2 n ] [ k ] f [ n ] [ k ] = f [ n ] [ k 1 ] [ k 1 = = S ( n ) ] = m f[2n][k]-f[n][k]=f[n][k-1]-[k-1==S(n)]=m n n .
比较容易看出 f [ n ] [ k 1 ] [ k 1 = = S ( n ) ] = f [ n 1 ] [ k 1 ] f[n][k-1]-[k-1==S(n)]=f[n-1][k-1] .

因为 f [ x ] [ k 1 ] f[x][k-1] 是随 x x 单调不降的,所以题目即求满足 f [ n 1 ] [ k 1 ] = m f[n-1][k-1]=m n n 的上下界,二分答案然后用数位DP检验即可。

由于此题k<=64,当m=0时n的上界为 2 63 2^{63} ,注意二分的范围。

Code:

#include<bits/stdc++.h>
#define LL unsigned long long
using namespace std;
int T,dig[65];
LL m,k,f[65][65];
LL dfs(int len,int s,bool fp){
	if(!len) return s==k;
	if(!fp&&~f[len][s]) return f[len][s];
	LL ret=0;int mx=fp?dig[len]:1;
	for(int i=0;i<=mx;i++) ret+=dfs(len-1,s+i,fp&&i==mx);
	if(!fp) f[len][s]=ret;
	return ret;
}
LL solve(LL n){
	int len=0;
	while(n) dig[++len]=n&1,n>>=1;
	return dfs(len,0,1);
}
int main()
{
	scanf("%d",&T);
	while(T--){
		memset(f,-1,sizeof f);
		scanf("%lld%d",&m,&k);
		if(k==1) {puts("-1");continue;}
		k--;
		LL l=1,r=1ull<<63,mid,L,R;
		while(l<r){
			mid=(l+r)>>1;
			if(solve(mid-1)<m) l=mid+1;
			else r=mid;
		}
		L=l,l=1,r=1ull<<63;
		while(l<r){
			mid=(l+r)>>1;
			if(solve(mid-1)<=m) l=mid+1;
			else r=mid;
		}
		R=l-1;
		printf("%llu %llu\n",L,R);
	}
}

猜你喜欢

转载自blog.csdn.net/C20181220_xiang_m_y/article/details/102690490