1. 程式人生 > >CodeForces-431D Random Task(二分答案+數位DP)

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

題意

給定 m m k k ,求一個 n n

使得 [ n + 1 , 2 n ] [n+1,2n]
範圍內的數中二進位制恰好有 k k 1 1 的數,恰有 m
m
個。
0 m 1 0 18 0 \leq m \leq 10^{18}
1 k 64 1 \leq k \leq 64

思路

有一個“顯然”的單調性, n n 越大, [ n + 1 , 2 n ] [n+1,2n] 中的數含有 k k 1 1 的數單調不減,無論 k k 的取值。
其實也不難證,當 n 1 n-1 變為 n n 時,區間從 [ n , 2 n 2 ] [n,2n-2] 挪到 [ n + 1 , 2 n ] [n+1,2n] 時,我們失去了 n n ,獲得了 2 n 1 , 2 n 2n-1,2n ,而 2 n 2n n n 的二進位制含有的 1 1 數相同,而多的那個 2 n 1 2n-1 又不會讓含有 k k 1 1 的數變少,由此得出單調性。
那就直接二分 n n ,用數位 dp \text{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;
}