CTU Open 2018 Lighting /// 组合数递推 二进制

题目大意:

给定n k 给定一个数的二进制位a[]

求这个数加上 另一个二进制位<=n的数b 后

能得到多少个不同的 二进制位有k个1 的数

 

样例
input
10 5
1000100111
output
13

10位的a 和 10位的b 相加得到c 
b取值范围是 0000000000~1111111111
所以 c取值范围是 1000100111~11000100110

也就是求在这个范围里 有5个1的数 有多少个
 
在这个取值范围里考虑两种情况
10位时>= 1000100111
11位时<=11000100110
 
(1)10位时>= 1000100111

要让数变大 考虑把0变为1 这样变化能保证得到的数绝对变大

对于第一个0  
10xxxxxxxx 变为11xxxxxxxx 
x里必须再有3个1才能符合5个1的要求 所以方案数是 C(8,3)
对于第二个0  100xxxxxxx 变为101xxxxxxx 
方案数是C(7,3)
对于第三个0  1000xxxxxx 变为1001xxxxxx
方案数是 C(6,3)

此时遇到了1 即到了10001xxxxx
因为要保证>= 1000100111
所以1必须固定不能变换
那么 继续看下一个0

100010xxxx 变为 100011xxxx
方案数是C(4,2)
1000100xxx 变为 1000101xxx
方案数是C(3,2)
然后1000100111本身也是一种方案

 

会发现其实就是在0位累加组合数

   1   0     0    0  1     0     0  1  1  1

        C(8,3)+C(7,3)+C(6,3)   +C(4,2)+C(3,2)        +1(本身)

 所以10位的可能方案有 56+35+20+6+3+1=121 

(2)11位时<=11000100110

要让数变小 就考虑把1变为0 
因为必须保证11位 所以默认第一位为1

对于第二个1
11xxxxxxxxx 变为 10xxxxxxxxx
剩下的x中需要再有4个1 所以方案数是C(9,4)
对于第三个1
110001xxxxx 变为 110000xxxxx
方案数是C(5,3)
对于第四个1
110001001xx 变为 110001000xx
方案数是C(2,2)
对于第五个1
1100010011x 变为 1100010000x
方案数是C(1,1)
然后11000100110本身也是一种方案

 
会发现其实就是在1位累加组合数

1  1  0  0  0  1  0  0  1     1  0

    C(9,4)         +C(5,3)      +C(2,2)+C(1,1)   +1(本身)

所以11位的可能方案有 126+20+1+1+1=149

 

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define mem(i,j) memset(i,j,sizeof(i))
#define inc(i,j,k) for(int i=j;i<=k;i++)
#define dec(i,j,k) for(int i=j;i>=k;i--)
const int N=1e3+5;
const int mod=1e9+7;

int n,k,a[N];
char s[N];
LL C[N][N];

void init() {
    C[0][0]=C[1][0]=C[1][1]=1LL;
    inc(i,2,N-1) {
        C[i][0]=1LL;
        inc(j,1,i-1) {
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        }
        C[i][i]=1LL;
    } 
}

int main()
{
    init();
    while(~scanf("%d%d%s",&n,&k,s)) {
        int cnt=0;
        inc(i,0,n-1) {
            if(s[i]=='0')a[i]=0;
            else  a[i]=1, cnt++;
        }

        if(k==0) {
            if(cnt==0) printf("1\n");
            else printf("0\n"); continue;
        }
        if(cnt==0) {
            printf("%d\n",C[n][k]); continue;
        }

        LL ans=0;
        int U=k-1, D=n-1;
        inc(i,0,n-1) {
            if(U<0) break;
            if(a[i]==1) U--;
            else ans=(ans+C[D][U])%mod;
            D--;
        }
        if(cnt<=k) ans=(ans+1LL)%mod; // 本身

        reverse(a,a+n);
        inc(i,0,n-1) {
            a[i]+=1;
            if(a[i]>1) a[i+1]++;
            a[i]%=2;
        }
        reverse(a,a+n+1);

        U=k-1, D=n-1;
        inc(i,1,n) {
            if(U==0) break;
            if(a[i]==1) {
                ans=(ans+C[D][U])%mod;
                U--;
            }
            D--;
        }
        cnt=0;
        inc(i,0,n) if(a[i]==1) cnt++;
        if(cnt>=k) ans=(ans+1LL)%mod; // 本身

        printf("%lld\n",ans);
    }

    return 0;
}
View Code

 

posted @ 2019-04-14 21:41  _Jessie  阅读(310)  评论(0编辑  收藏  举报