[CF431D]Random Task
题目
题解
由于题目要求的 \(n\) 十分巨大(\(n\le 10^{18}\)),并且要求恰好有 \(m\) 个数二进制下有 \(k\) 个 \(1\),考虑二分(先猜后证
但是如果要使用二分,需要证明单调性,我们记 \(f(x)\) 为 \(x+1\) 到 \(2x\) 之间的数中,他们二进制表示下恰好有 \(k\) 个 \(1\) 的数字个数,如果有单调性,显然是 \(f(x)\le f(x+1)\),如何证明?
由于 \(f(x)\) 是区间 \([x+1,2x]\) 的个数,\(f(x+1)\) 是区间 \([x+2,2x+2]\) 的个数,去除相同的部分,\(f(x)\) 就剩下 \(x+1\),而 \(f(x+1)\) 剩下 \(2x+1,2x+2\),由于 \(x+1\) 与 \(2x+2\) 的 \(1\) 的个数相同(相当于左移一位),那么如果 \(2x+1\) 满足条件,\(f(x)=f(x+1)-1\),反之 \(f(x)=f(x+1)\),也就是说,无论如何都有 \(f(x)\le f(x+1)\),可证其单调性。
考虑目前二分到一个 \(n\),我们如何计算 \(f(n)\)?考虑记 \(g(n)\) 为 \([1,n]\) 中满足条件的数,那么 \(f(n)=g(2n)-g(n)\),而 \(g(n)\) 可以考虑使用数位 \(DP\) 来做。
优化:其实 \(g(x)\) 可以使用组合数进行优化,这样也是合理的。
代码
#include<cstdio>
#include<cstring>
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long
#define cg (c=getchar())
template<class T>inline void read(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T read(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int maxsize=64;
LL m;int k;
inline void Init(){
m=read(1ll),k=read(1);
}
int a[maxsize+5];
LL f[maxsize+5][maxsize+5][2];
LL C[maxsize+5][maxsize+5];
LL Dfs(const int pos,const int cnt,const int rl){
if(~f[pos][cnt][rl])return f[pos][cnt][rl];
if(cnt>k)return 0;
if(pos==0)return cnt==k;
if(!rl)return C[pos][k-cnt];
int up=rl?a[pos]:1;
LL& ret=f[pos][cnt][rl];ret=0;
rep(i,0,up)ret+=Dfs(pos-1,cnt+i,rl && (i==a[pos]));
return ret;
}
inline LL calc(const LL n){
int len=0;LL tmp=n;
for(;tmp;tmp>>=1)a[++len]=tmp&1;
memset(f,-1,sizeof f);
// printf("When n == %lld, calc(%lld) == %lld\n",n,n,Dfs(len,0,1,1));
return Dfs(len,0,1);
}
inline void Bisearch(){
LL l=1,r=1e18,mid;
while(l<r){
// printf("Now l == %lld, r == %lld\n",l,r);
mid=l+r>>1;
if(calc(mid<<1)-calc(mid)>=m)r=mid;
else l=mid+1;
}writc(l,'\n');
}
inline void Get_C(){
rep(i,1,60){
C[i][0]=C[i][i]=1;
rep(j,1,i-1)C[i][j]=C[i-1][j-1]+C[i-1][j];
}
}
signed main(){
Init();
Get_C();
Bisearch();
return 0;
}