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;
}

 

posted @ 2013-05-01 22:03  心向往之  阅读(186)  评论(0)    收藏  举报