CTU Open 2018 Lighting /// 組合數遞推 二進制

題目大意:c++

給定n k 給定一個數的二進制位a[]ide

求這個數加上 另外一個二進制位<=n的數b 後spa

能獲得多少個不一樣的 二進制位有k個1 的數code

 

樣例
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 這樣變化能保證獲得的數絕對變大blog

對於第一個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必須固定不能變換
那麼 繼續看下一個0input

100010xxxx 變爲 100011xxxx
方案數是C(4,2)
1000100xxx 變爲 1000101xxx
方案數是C(3,2)
而後1000100111自己也是一種方案it

 

會發現其實就是在0位累加組合數event

   1   0     0    0  1     0     0  1  1  1class

        C(8,3)+C(7,3)+C(6,3)   +C(4,2)+C(3,2)        +1(自己)cli

 因此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
相關文章
相關標籤/搜索