数位类统计问题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; }
相关文章推荐
- IntentFilter
- 第二次站立会议4
- bzoj1452 二维树状数组
- Qt+VS2010的配置
- 双向队列
- 湖南卫视直播tv
- Python-django安装
- HC-SR501 人体红外感应模块
- LaTeX学习教程 持续更新
- php学习整理之mysql(三)配置php使其支持mysql
- 二叉树中和为某一值的路径
- 快速幂取模模板 && 51nod 1013 3的幂的和
- 品读程序员晋级之路
- 走迷宫
- Ubuntu 14.04 启用休眠
- 第二次站立会议3
- LeetCode:Count Primes
- phpstudy在linux下的初体验-----php文件放置目录
- Ubuntu的挂起和休眠
- Android源码项目目录结构