2019ICPC南昌 C And and Pair dp(二项式定理 / dp)

给定一个非常大的非负整数n,计算满足以下条件的整数对 ( i   , j ) (i\,,j) (i,j)的个数:
1 、 0 ≤ j ≤ i ≤ n ; 1、0≤j≤i≤n; 10jin;
2 、 i & n = i ; a n d 2、i\&n=i;and 2i&n=i;and
3 、 i & j = 0 3、i\&j=0 3i&j=0

二项式

在二进制的情况下,
因为 i & n = i i\&n=i i&n=i,所以:只有当n某一位为1时,i在这一位才能为1。
因为 i & j = 0 i\&j=0 i&j=0,所以:当i某一位为1时,j在这一位只能为0;当i某一位为0时,(保证 j < i j<i j<i的情况下)j在这一位可以为0也可以为1。

为保证 j ≤ i j≤i ji以及整数对不重合,我们可以从低位到高位,算出i的每一位固定为1时(当然是前提可以为1时),整数对 ( i   , j ) (i\,,j) (i,j)的个数:

如:n=10010110010110,我们固定i中倒数第8位为1,即0000001xxxxxxx。
在n的后面的7位中有3位为1。当i中这三位都为1时:因为i某一位为0时,j在这一位可以为0也可以为1,所以整数对个数为 C 3 3 × 2 4 C_{3}^3×2^4 C33×24;当i中有两位为1时,整数对个数为 C 3 2 × 2 5 C_{3}^2×2^5 C32×25;当i中有一位为1时,整数对个数为 C 3 1 × 2 6 C_{3}^1×2^6 C31×26;当i后面7位全为0时,整数对个数为 C 3 0 × 2 7 C_{3}^0×2^7 C30×27

s u m = C 3 0 × 2 7 + C 3 1 × 2 6 + C 3 2 × 2 5 + C 3 3 × 2 4 = C 3 0 × 2 4 + C 3 1 × 2 5 + C 3 2 × 2 6 + C 3 3 × 2 7 = 2 4 ( C 3 0 × 2 0 + C 3 1 × 2 1 + C 3 2 × 2 2 + C 3 3 × 2 3 ) sum=C_{3}^0×2^7+C_{3}^1×2^6+C_{3}^2×2^5+C_{3}^3×2^4=C_{3}^0×2^4+C_{3}^1×2^5+C_{3}^2×2^6+C_{3}^3×2^7=2^4(C_{3}^0×2^0+C_{3}^1×2^1+C_{3}^2×2^2+C_{3}^3×2^3) sum=C30×27+C31×26+C32×25+C33×24=C30×24+C31×25+C32×26+C33×27=24(C30×20+C31×21+C32×22+C33×23),通过观察可以发现前面幂的次数是前面n中0的个数,组合数的个数是前面n中1的个数。然后我们可以用二项式定理求出后面的一项:
在这里插入图片描述
或者找规律:通过下面规律递推就可以得出 ∑ i = 0 n C n i × 2 i \sum _{i=0}^n C _{n}^i×2^i i=0nCni×2i的值
在这里插入图片描述

#include<algorithm>
#include<iostream>
//#include<stdio.h>
#include<vector>
#include<string>
#include<cstring>
#include<map>
using namespace std;
#define int long long
const int manx=1e5+10;
const int manx2=1e7+10;
const int mod=1e9+7;

signed main()
{
    ios::sync_with_stdio(false);
    int t;
    char str[manx];
    cin>>t;
    while(t--)
    {
        cin>>str;
        int ans=0,mul=1,temp=1;
        int len=strlen(str);
        for(int i=len-1;i>=0;i--)
        {
            if(str[i]=='1')
            {
                (ans+=(mul*temp))%=mod;
                (temp+=temp*2)%=mod;
            }
            else
                (mul*=2)%=mod;
        }
        cout<<ans+1<<endl;//还有i和j都为0的情况
    }
    return 0;
}

dp

根据:
1、只有当n某一位为1时,i在这一位才能为1;2、当i某一位为1时,j在这一位只能为0;当i某一位为0时,(保证 j < i j<i j<i的情况下)j在这一位可以为0也可以为1。
直接递推,碰到n某一位为1时,加上dp[ ][1][0]的值(同前面说的i某位固定为1是,整数对的个数)

#include<algorithm>
#include<iostream>
//#include<stdio.h>
#include<vector>
#include<string>
#include<cstring>
#include<map>
using namespace std;
#define int long long
const int manx=1e5+10;
const int manx2=1e7+10;
const int mod=1e9+7;

int dp[manx][2][2];
signed main()
{
    ios::sync_with_stdio(false);
    int t;
    char str[manx];
    cin>>t;
    while(t--)
    {
        cin>>str;
        memset(dp,0,sizeof dp);
        int ans=0,len=strlen(str);
        if(str[len-1]=='1')
            dp[len-1][1][0]=dp[len-1][0][1]=dp[len-1][0][0]=1;
        else dp[len-1][0][1]=dp[len-1][0][0]=1;
        ans+=dp[len-1][1][0];
        for(int i=len-2;i>=0;i--)
        {
            if(str[i]=='1')
                dp[i][1][0]=dp[i][0][1]=dp[i][0][0]=(dp[i+1][1][0]+dp[i+1][0][1]+dp[i+1][0][0])%mod;
            else dp[i][0][1]=dp[i][0][0]=(dp[i+1][1][0]+dp[i+1][0][1]+dp[i+1][0][0])%mod;;
            (ans+=dp[i][1][0])%=mod;
        }
        cout<<ans+1<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值