CTU Open 2018 Lighting /// 组合数递推 二进制
题目大意:
给定n k 给定一个数的二进制位a[]
求这个数加上 另一个二进制位<=n的数b 后
能得到多少个不同的 二进制位有k个1 的数
10 5
1000100111
10位的a 和 10位的b 相加得到c
b取值范围是 0000000000~1111111111
所以 c取值范围是 1000100111~11000100110
也就是求在这个范围里 有5个1的数 有多少个
要让数变大 考虑把0变为1 这样变化能保证得到的数绝对变大
对于第一个0此时遇到了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
要让数变小 就考虑把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 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; }