poj 3252 组合计数
题意:给出两个数a和b,求出[a,b]之间,有多少个数为round number。round number的定义:其二进制数中0的数量大于或等于1的个数。
分析:设round number有 s 位,则0的个数至少为 c = (s+1)/2
1. 若a的二进制有pa位,b有pb位,(pa<s<pb),s位的数中,第一位为1,其余s-1位可自由选择,所以有sum = C(s-1,c ) + C(s-1,c+1 ) +...+ C(s-1,s-1 )。
2. 和a位数相同且大于a的round number的个数为:
将第一个0改为1,则后面的数位自由组合,满足round number条件的话,这个数一定大于a。
将第二个0改为1,后面的数位自由组合,满足条件后,这个数也大于a。
第三个0改为1......
一共需要 t = (pa+1)/2 个 0,设a[i]为第c+1个0,改为1,则前面已经有c个0,至少还需要temp=t-c个0,由于第一位一定是1,则有n=len-i-1个位置可选
则个数为sum += C(n,temp) + C(n,temp+1) + C(n,temp+2) + ...+C(n,n) .
若a本身是round number,sum=sum+1.
对于b,求出大于b的个数为ss,pb位的round number一共有rr,则小于等于b的有ss-rr位。
const int maxn = 40; int C[maxn][maxn]; void init(){ FOR(i,0,maxn){ C[i][0] = 1; C[i][1] = i; FOE(j,2,i) C[i][j] = C[i-1][j] + C[i-1][j-1]; } } int a[maxn],b[maxn],pa,pb,cnt; void CH(int num,int arr[],int &pos){//将num转换为二进制,存入arr[]中 pos = 0; int i = 0; while(i<32 && (num & 1<<(31-i)) == 0)i++; while(i<32) arr[pos++] = num & 1<<(31-i++); } int solve(int arr[],int pos,int flag){ int cnt = 0, c, c0 = 0, t = (pos+1)/2; FOR(i,1,pos) if(arr[i] == 0){ c = pos - i - 1; int temp = t - c0 ; if(temp < 0) temp = 0; FOE(j, temp, c) cnt += C[c][j]; c0++; } int f = c0 >= t; if(flag) return cnt + f; int sum = 0; FOR(i,t,pos) sum += C[pos-1][i]; return sum - cnt; } int main(){ #ifndef ONLINE_JUDGE //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); #endif init(); unsigned int aa,bb; cin>>aa>>bb; CH(aa, a, pa); CH(bb, b, pb); FOR(j,pa+1,pb) FOR(k,(j+1)/2,j) cnt += C[j-1][k]; cnt += solve(a,pa,1) + solve(b,pb,0); cout<<cnt<<endl; return 0; }