[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;
}
posted @ 2020-08-11 11:11  Arextre  阅读(117)  评论(0编辑  收藏  举报