2069: Saruman’s Level Up(排列组合+分段打表)

传送门:点击打开链接

题意:就是你在建一个塔,开始它是0层,当某一天的天数,转化为2进制,每一位为1的个数是3的倍数,就可以把塔升级一层,比如第7天,它的二进制为111,3个1,塔就可以升级一层,比如第64天,它的二进制为111111,6个1,塔也可以升级一层。 输入一个天数,问你当天,塔的层数是多少。

思路:n为10^16,二进制最多54位,组合数不会爆long long,首先考虑2的n(1<=n<=55)次幂数,也就是二进制形式为1,10,100...的情况,用组合数求出这些数的答案。然后再加减去重得答案,表达能力有限,可以结合代码理解。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 55;
ll a[N],n,bit[N];

ll gb(ll x){
    ll tp[N],k=0;
    while(x){
        tp[k++]=x%2;
        x/=2;
    }
    for(ll i=k-1;i>=0;i--)
        bit[k-1-i]=tp[i];
    return k;
}

ll C(ll n,ll m){
    if(m==0) return 1;
    if(n<0||m<0||n<m) return 0;
    ll s=1;
    for(ll i=1;i<=m;i++)
        s=s*(n-i+1)/i;
    return s;
}

void init(){
    a[1]=a[2]=a[3]=0;
    ll s=0;
    for(int i=4;i<N;i++){
        for(int j=2;j<=i-2;j+=3)
            s+=C(i-2,j);
        a[i]=s;
    }
}

int main(){
    ///cout<<C(59,29)<<endl;
    init();
    while(cin>>n){
        ll m=n+1;
        ll bc=gb(m),ans=0,ct=0;
        ans+=a[bc];
        bc=gb(n);
        for(ll i=0;i<bc;i++)
            if(bit[i]==1) ct++;
        if(ct==1||ct==bc) {
            printf("Day %lld: Level = %lld\n",n,ans);
            continue;
        }
        for(ll i=2;i<bc;i+=3) ans+=C(bc-1,i);
        ll idx=0,ct0=0,ct1=0;
        while(idx<bc){
            while(bit[idx]&&idx<bc) ct1++,idx++;
            while(bit[idx]==0&&idx<bc) ct0++,idx++;
            for(ll i=1;i<=ct0;i++)
                for(ll j=3;j<=bc;j+=3)
                    ans-=C(ct0,i)*C(bc-idx,j-ct1-i);
            ct0=0;
        }
        printf("Day %lld: Level = %lld\n",n,ans);
    }
    return 0;
}



猜你喜欢

转载自blog.csdn.net/tianwei0822/article/details/80007411