2019 ICPC 南昌区域赛 - C And and Pair(思维+组合数学)

题目链接


题目大意

给一个二进制数 n n n,求满足 0 ≤ j ≤ i ≤ n 0 \leq j \leq i \leq n 0jin ( i & n = i )    & &    ( i & j = 0 ) (i \&n=i) ~~ \&\& ~~(i\&j=0) (i&n=i)  &&  (i&j=0)的数对 ( i , j ) (i,j) (i,j)有多少对, n n n可能有前导 0 0 0

解题思路

开始推了样例,首先肯定要去找 i i i,以第二个样例来看,若要满足 i & 1010 = i i\&1010=i i&1010=i,我们从左向右按位来看,则 i i i的第一位可能是 1 1 1或者 0 0 0,只有这样做完运算后这一位才不会变。然后是第二位,我们知道 i i i的这一位必须是 0 0 0,否则该位就发生了改变。然后得出结论。求 i i i时, S S S对应的每一位若为 1 1 1该位则有两种,若为 0 0 0则该位只有一种。因此可以求出所有的 i i i(还知道,这里求出的 i i i肯定都是小于等于 S S S的)。然后求 j j j,先忽略求出 i i i的前导 0 0 0,然后简单推导可以知道 j j j对应的规律和求 i i i时刚好相反,即若非前导 0 0 0的某一位为 0 0 0,则对应两种情况,若为 1 1 1则对应一种情况。貌似思路有了,可不会写,还是太菜~~

正解思路:从右往前看,每一位遇到 1 1 1就让他做 i i i的第一位。看除了第一位后面有多少个 0 0 0有多少个 1 1 1,设这个 1 1 1的低位中有 m m m 1 1 1 n n n 0 0 0,推导得出有 2 n ∗ ∑ C m i 2 i 2^n* \sum C_m^i2^i 2nCmi2i种方案。这个式子能不能化简呢?答案是能:

  • ∑ C n i ∗ 2 i = 3 n \sum C_n^i*2^i=3^n Cni2i=3n
    证明: ( x + 1 ) n = C n 0 ∗ x 0 + C n 1 ∗ x 1 + C n 2 ∗ x 2 + ⋅ ⋅ ⋅ + C n n − 1 ∗ x n − 1 + C n n ∗ x n (x+1)^n=C_n^0*x^0+C_n^1*x^1+C_n^2*x^2+···+C_n^{n-1}*x^{n-1}+C_n^n*x^n (x+1)n=Cn0x0+Cn1x1+Cn2x2++Cnn1xn1+Cnnxn
    x = 2 x=2 x=2,则有 3 n = C n 0 ∗ 2 0 + C n 1 ∗ 2 1 + C n 2 ∗ 2 2 + ⋅ ⋅ ⋅ + C n n − 1 ∗ 2 n − 1 + C n n ∗ 2 n 3^n=C_n^0*2^0+C_n^1*2^1+C_n^2*2^2+···+C_n^{n-1}*2^{n-1}+C_n^n*2^n 3n=Cn020+Cn121+Cn222++Cnn12n1+Cnn2n

还要注意,第一位我们最后是没有考虑它为0的情况,第一位为0的话后面只能都是0,这样最后再加一即可

代码:

#include <iostream>
#include <string>
using namespace std;
typedef long long ll;
const int Mod=1e9+7;
const int maxn=1e5+10;
ll quick_mod(ll x,ll n){
    ll ans=1;
    while(n){
        if(n&1) ans=ans*x%Mod;
        n>>=1;
        x=x*x%Mod;
    }
    return ans;
}
int main()
{
    int T;
    string str;
    scanf("%d",&T);
    while(T--){
        string s="";
        cin>>str;
        int k=0;
        while(str[k]=='0') ///处理初始串的前缀0
            k++;
        for(int i=k;i<str.size();i++)
            s+=str[i];
        //cout<<s<<endl;
        int num1=0,num2=0;
        ll ans=0;
        for(int i=s.size()-1;i>=0;i--){
            if(s[i]=='1'){
                ans=(ans%Mod+(quick_mod(2,num1)*quick_mod(3,num2))%Mod)%Mod;
                num2++;
            }else num1++;
        }
        ans=(ans+1)%Mod; ///注意
        printf("%lld\n",ans);
    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值