POJ 3252 Round Number (排列组合)

题目链接

定义Round Number为二进制表示下0不少于1的数字。问区间[l,r]内有多少个Round Number。

仍然是考虑用[1,r]的答案减去[1,l-1]的答案。对于一个二进制长度为n的数x,显然长度为1~n-1的数必然是合法的。

首先看长度为n且小于x的数中有多少个合法答案。方法是从第二位开始,如果碰见1,就考虑如果它为0,可能产生多少种合法答案。以26=(11010)b为例,第二位是1,考如果它为0,此时有1个1和1个0,后面的3位里应有2个或3个0,则方案数C(3,2)+C(3,3)。然后到了第4位,如果它为0,此时已有2个1和2个0,最后一位要有1个0,方案数C(1,1)。依此类推。

然后计算二进制长度为x的数中有多少个是合法答案。首先第1位必然为1,则后面可能有1,2……(x-1)/2个1,即答案为C(x-1,1)+C(x-1,2)+……+C(x-1,(x-1)/2)。当x-1为奇数的时候答案为2^(x-2),x-1为偶数的时候答案为(2^(x-1)-C(x-1,(x-1)/2))/2。

/*嘤嘤嘤*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1050;
const ll INF = (1LL << 62) - 1;
const ll mod = 998244353;

int a, b;
ll C[35][35];
int num[35];

void init()
{
    C[0][0] = C[1][0] = C[1][1] = 1;
    for(int i = 2;i < 35;i++)
    {
        C[i][0] = 1;
        for(int j = 1;j < i;j++)
            C[i][j] = C[i-1][j-1] + C[i-1][j];
        C[i][i] = 1;
    }
}

ll getnum(int x)
{
    x--;
    if(x % 2 == 0) return ((1LL << x) - C[x][x/2])/2;
    else return (1LL << x)/2;
}

ll cal(int n)
{
    if(n <= 1) return 0;
    int no = 0, na = 0, nb = 0;
    while(n)
    {
        num[no++] = n % 2;
        if(n % 2) nb++;
        else na++;
        n >>= 1;
    }
    ll ans = 0;
    for(int i = 1;i <= no - 1;i++)
        ans += getnum(i);
    if(na >= nb) ans++;
    na = 0, nb = 1;
    for(int i = no - 2;i >= 0;i--)
    {
        if(num[i] == 1)
        {
            for(int j = i;j >= 0 && na + 1 + j >= nb + i - j;j--)
                ans += C[i][j];
            nb++;
        }
        else na++;
    }
    return ans;
}

int main()
{
    init();
    scanf("%d%d", &a, &b);
    //cout<<cal(b) <<" "<<cal(a-1)<<endl;
    printf("%lld\n", cal(b) - cal(a-1));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/NPU_SXY/article/details/82800971
今日推荐