您的位置:首页 > 其它

数位类统计问题ural 1057

2015-06-16 21:54 369 查看

题目大意

求给定区间[X,Y]中满足下列条件的整数个数:这个数恰好等于K个互不相等的B的整数次幂之和。例如,设X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意:

17=24+2017 = 2^4+2^0

18=24+2118 = 2^4+2^1

20=24+2220 = 2^4+2^2

输入:第一行包含两个整数X 和Y。接下来两行包含整数K 和B。

输出:只包含一个整数,表示满足条件的数的个数。

数据规模:1≤X≤Y≤231−1,1≤K≤20,2≤B≤101 \le X \le Y \le 2^{31}−1,1 \le K \le 20, 2 \le B \le 10。

解题思路

所求的数为互不相等的幂之和,亦即其B进制表示的各位数字都只能是0和1。我们只需讨论二进制的情况,然后其他进制可以转换成二进制来求解。

首先注意到本题具备区间减法性,即count(i…j) = count(0…j)-count(0…i-1),那么接下来的研究重点就是如何求0~n之间有多少符合要求的数。

假设n有i位,它的二进制形式是(ai−1ai−2...a1a0)2(a_{i-1}a_{i-2}...a_1a_0)_2。我们从高位向低位扫描它,当某一位aja_j是0时则继续向下看,当aja_j为1时计数。计数方法如下:

遍历过程中要维护一个值tot,记录目前为止遍历到几个1

看到aja_j为1后,给ans累加上(k−totj)\binom{k-tot}{j}。它的意义是:把aja_j置0,然后右边j位选k-tot个变为1,这样的数显然满足“小于等于n且含k个1”。最后把tot++。

剩下的工作就是预处理组合数F(n,m)了,二进制的情形讨论完毕。接下来讨论其他进制与二进制的转换。

其他进制与二进制的一个明显区别是该进制下的X和Y某一位可能大于1,我们必须把它变成全01的形式,然后套用二进制情形的解法。

转换方法一

把X转化为“大于等于X且B进制下每位都是0或1且最接近X的数”,把Y转化为“小于等于Y且B进制下每位都是0或1且最接近Y的数”。设它们分别是nx和ny,如果我们已经实现了二进制情形的统计函数f(x,y,k),那么f(nx,ny,k)就是B进制下的答案。

转换方法二

把X和Y都转化为“小于等于它且B进制下每位都是0或1且最接近它的数”,这样只需要写一个转化函数。不过这种情况下求得f(nx,ny,k)后要特判一种情况:如果(X)B(X)_B含大于1的位而(nx)2(nx)_2又恰好含k个1,这时候答案会多一,因此要把ans-1。

代码

[code]#include <cstdio>

int F[33][33];

void init()
{
    F[0][0] = 1;
    for(int i=1; i<=30; i++)
    {
        F[i][0] = F[i-1][0];
        for(int j=1; j<=i; j++)
            F[i][j] = F[i-1][j] + F[i-1][j-1];
    }
}

int calculate(int n, int k)
{
    int ans = 0, tot = 0;
    if(n == 0) return 0;

    for(int i=30; i>=0; i--)
    {
        if(n & (1<<i))
        {
            ans += F[i][k-tot];
            tot ++;
        }
        if(tot > k) break;
    }
    if(tot == k) ans ++;
    return ans;
}

// 求B进制形式下,所有小于n且所有位都为0或1中的最大数
int change(int n, int B)
{
    int num[33], top = -1;
    int p, ans;
    while(n)
    {
        num[++top] = n % B;
        n /= B;
    }
    // 在n的B进制串中从高到低找到第一个不为0、1的位
    // 把它置为1,然后后面全为1
    for(p=top; p>=0 && num[p]<=1; p--);
    ans = 0;
    for(int j=top; j>p; j--)
        if(num[j])
            ans += (1<<j);
    if(p >= 0)
        ans += (1<<(p+1))-1;
    return ans;
}

bool special(int x, int nx, int B, int k)
{
    /// nx = change(x, B)
    bool flag = true; // x的B进制串中只含01
    while(x)
    {
        if(x % B > 1)
        {
            flag = false;
            break;
        }
        x /= B;
    }
    int tot = 0; // 统计x二进制串中1的个数
    while(nx)
    {
        if(nx & 1) tot ++;
        nx >>= 1;
    }
    if(tot == k && !flag)
        return true;
    return false;
}

int main()
{
    int x,y,k,B, ans;
    init(); // 预处理组合数
    while(scanf("%d%d", &x,&y) != EOF)
    {
        scanf("%d%d", &k,&B);
        if(B > 2)
        {
            int nx = change(x, B);
            int ny = change(y, B);
            ans = calculate(ny, k) - calculate(nx-1, k);

            // 特判一种情况
            if(special(x, nx, B, k))
                ans --;
        }
        else ans = calculate(y, k) - calculate(x-1, k);
        printf("%d\n", ans);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: