[机房测试]数字谜题

Posted tqr06

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[机房测试]数字谜题相关的知识,希望对你有一定的参考价值。

小 X 同学有很强的计算能力,现在他正在玩一个游戏。
现在有一个正整数 x,每次操作他会将当前的数变为这个数写成二进制后 1 的个数
小 X 不断的进行操作,直到这个数变成 1 为止
由于小 X 的计算能力很强,他现在给出一 n 
他想知道有多少不超过 n 的正整数会在 k 次操作后变成 1 
由于答案可能很大,请对 1000000007 取模

因为数据范围是\(2^1000\),所以一次操作后最多就只有1000个1了

因此直接暴力处理出1~1000所有数变为1的操作次数,把次数为 \(k-1\) 的数字加入待处理栈,记作数 \(i\)

接下来的任务就是找到从1~n中有哪些数字的二进制中含 \(i\)\(1\)

方法就是一个很简单的数学组合,对于一个数字,如果某一位为1,

那么对答案的贡献就是 \(C_pos-1^i-cnt\)

\[ans=\SigmaC_pos_j-1^i-cnt(i=num[1],num[2],...,num[size])\]

其中 \(pos\) 表示第几位是 \(1\)\(cnt\) 表示前面的1的个数

那么我们就可以愉快地计算答案了

很坑的是 \(k=1,0\) 的时候要特判

#include<bits/stdc++.h>
#define mod 1000000007
#define ll long long
#define lowbit(x) (x&-x)
using namespace std;

int k;
int num[1005],change;//num是把这个数字变为1的操作次数 
vector<int> v;
char c[1005];
ll ans=0;

int one_num(int x)

    int res=0;
    while(x) res++;x-=lowbit(x);
    return res;


ll fac[1005],invfac[1005];
ll qpow(ll n,ll k)

    ll res=1;
    while(k)
    
        if(k&1) res=(res*n)%mod;
        n=(n*n)%mod;
        k>>=1;
    
    return res;

void init()

    fac[0]=1;
    for(register int i=1;i<=1002;++i) fac[i]=(1LL*fac[i-1]*i)%mod;

ll inv(ll x)return qpow(x,mod-2);
ll C(int n,int m)if(m>n||m<0) return 0;return ((1LL*fac[n]*inv(fac[m])%mod)*inv(fac[n-m]))%mod;


int pos[1005],top=0;

int main()

    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    init();
    scanf("%s",c+1);
    scanf("%d",&k);
    if(k==0) puts("1");return 0;
    int len=strlen(c+1);
    num[1]=0;
    for(register int i=2;i<=len;++i)
    
        int j=i;
        while(true)
        
            change=one_num(j);
            num[i]++;
            if(change==1) break;
            j=change;
        
    
//  for(register int i=1;i<=len;++i) cout<<i<<" need: "<<num[i]<<endl;
    for(register int i=1;i<=len;++i) if(num[i]==k-1) v.push_back(i);
    
    //下面是计算 1~n 中有多少个数二进制中含 i 个 1 
//  cout<<"size:"<<v.size()<<endl;
    for(register int i=len;i>=1;--i)
        if(c[i]=='1') pos[++top]=len-i+1;
    while(!v.empty())//循环每个i 
    
        ll i=v.back();
        ll topp=top;
        while(topp)
        
            ans=(ans+C(pos[topp]-1,i-(top-topp)))%mod;
//          cout<<"C "<<pos[topp]-1<<" "<<i-(top-topp)<<" "<<C(pos[topp]-1,i-(top-topp))<<endl;
            topp--;
        
        int cnt=0;
        for(register int i=1;i<=len;++i) if(c[i]=='1') cnt++;
        if(cnt==i) ans=(ans+1)%mod;
        v.pop_back();
    
    printf("%lld\n",ans-(k==1));
    return 0;

以上是关于[机房测试]数字谜题的主要内容,如果未能解决你的问题,请参考以下文章

谜题29:循环者的新娘

竖式谜题

谜题33:循环者遇到了狼人

谜题18:字符串奶酪

LeetCode——滑动谜题

图扑数字孪生数据中心机房,助力产业绿色低碳转型