• [20190921机房测试] 数字谜题


    小 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=Sigma{C_{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
    ",ans-(k==1));
    	return 0;
    }
    
  • 相关阅读:
    苹果开发者账号多少钱?个人/公司/企业申请费用及怎么选【都有】
    uniapp ios真机调试【亲测有效】
    Uniapp---IOS打包证书私钥密码怎么获取?
    微信小程序地图计算两个点之间的距离
    各大地理坐标系互转
    解决mac下vscode等应用中vim光标无法快速移动
    python 脚本如何在后代运行并记录标准输出
    wkhtmltox 在Linux上安装
    shell中的##*,%%*问题
    matplotlib、seaborn 展示中文字体
  • 原文地址:https://www.cnblogs.com/tqr06/p/11562893.html
Copyright © 2020-2023  润新知