CodeForces-431D Random Task(二分答案+数位DP)

探讨了在[n+1,2n]范围内,寻找恰好有k个1的二进制数,使得这类数的数量等于m的问题。通过证明单调性,采用二分查找结合数位动态规划的方法求解。

题意

给定 mmmkkk ,求一个 nnn 使得 [n+1,2n][n+1,2n][n+1,2n] 范围内的数中二进制恰好有 kkk111 的数,恰有 mmm 个。
0≤m≤10180 \leq m \leq 10^{18}0m1018
1≤k≤641 \leq k \leq 641k64

思路

有一个“显然”的单调性,nnn 越大,[n+1,2n][n+1,2n][n+1,2n] 中的数含有 kkk111 的数单调不减,无论 kkk 的取值。
其实也不难证,当 n−1n-1n1 变为 nnn 时,区间从 [n,2n−2][n,2n-2][n,2n2] 挪到 [n+1,2n][n+1,2n][n+1,2n] 时,我们失去了 nnn,获得了 2n−1,2n2n-1,2n2n1,2n ,而 2n2n2nnnn 的二进制含有的 111 数相同,而多的那个 2n−12n-12n1 又不会让含有 kkk111 的数变少,由此得出单调性。
那就直接二分 nnn,用数位dp\text{dp}dp验证答案即可。

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=(y);++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=(y);--i)
typedef long long LL;
using namespace std;
LL dp[70][70],m;
int num[70],K;

LL dfs(int k,int status,bool ismax)
{
	if(k==0)return status==K;
	if(~dp[k][status]&&!ismax)return dp[k][status];
	int maxer=ismax?num[k]:1;LL res=0;
	FOR(i,0,maxer)res+=dfs(k-1,status+i,ismax&&i==maxer);
	if(!ismax)dp[k][status]=res;
	return res;
}

LL solve(LL k)
{
	int n=0;
	while(k)
	{
		num[++n]=k%2;
		k>>=1;
	}
	return dfs(n,0,1);
}

int main()
{
	scanf("%lld%d",&m,&K);
	memset(dp,-1,sizeof(dp));
	LL L=1,R=1e18;
	while(L<R)
	{
		LL mid=L+R>>1;
		if(solve(mid*2)-solve(mid)>=m)
		{
			R=mid;
		}
		else L=mid+1;
	}
	printf("%lld\n",L);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值