数位统计小结

    “在信息学竞赛中,有这样一类问题:求给定区间中,满足给定条件的某个D 进制数或此类数的数量。所求的限定条件往往与数位有关,例如数位之和、指定数码个数、数的大小顺序分组等等。题目给定的区间往往很大,无法采用朴素的方法求解。此时,我们就需要利用数位的性质, 设计log(n)级别复杂度的算法。 解决这类问题最基本的思想就是 “逐位确定”的方法。下面就让我们通过几道例题来具体了解一下这类问题及其思考方法。”——09集训队论文《浅谈数位类统计问题》by 刘聪

 

以前碰到过一些数位统计的题目,往往这类题目比较让人恼火,做这类题的时候总是有一种力不从心的感觉,如果设计出来的算法不漂亮那将意味着有众多的细节需要一一特判之,比如去年的zjoi就有一道统计区间[l,r]中的数的每一位上0~9出现了多少次,结果这道题我整整做了两个小时才把思路理顺清楚。

 

这篇论文向我介绍了一种非常形象的的思考及算法方向,那就是数形结合。数字往往好深入分析但是太过于抽象,形则直观明了但是难以究其根源,很好的结合两者将会使得思考复杂度大大降低。

 

这篇论文一共介绍了5到例题,ly还在poj上也找了一道数位统计题,至今还剩论文中的最后一题没有AC==。

 

1.Amount of degrees (ural 1057)

题目大意:
求给定区间[X,Y]中满足下列条件的整数个数:这个数恰好等于K 个互不相等的 B的整
数次幂之和。例如,设X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意:
17 = 10001
18 = 10010
20 = 10100
输入:第一行包含两个整数X 和 Y。接下来两行包含整数 K 和 B。
输出:只包含一个整数,表示满足条件的数的个数。
数据规模:1 ≤ X ≤ Y ≤ 231−1,1 ≤ K ≤ 20,  2 ≤ B ≤ 10。

 

分析:

对于B>2的所有询问都可以转化成B=2,由浅入深的先考虑B=2的情况。

因为满足区间减法,所以问题就转化为统计[0,n]中的2进制数中1的个数为k的数的个数。

再简单一点,如果n是2x-1的话答案是什么?就是C(k,x)。

那么能不能把[0,n]拆成若干个2x-1呢?可以的,如果我们把这里面的数字看成一颗2叉树,那么这颗二叉树肯定是由不超过logN个满二叉树构成的,所以我们可以把一个问题拆分成logN个问题分别求解。

 

2.Sorted bit sequence (spoj 1182)

题目大意

求给定区间[L,R]中第k大的数,两个数的大小关系被定义为:对应的二进制数中的1的个数为第一关键字,大小为第二关键字。

 

分析:

首先直接枚举第K大的数的1的个数,问题转化为求区间[L,R]中1的个数为X的数的个数,见第一题。

枚举出第K大的数的一的个数之后,在二分答案即可,同第一题。

 

3.Apocalypse Someday(poj3208)

题目大意

求第K大的包含666的数字

 

分析:依然是考虑逐位统计,预处理出F[I,J]表示还有i位没有确定,之前有连续J个6的数有多少个,枚举每一位即可。

 

4.Sequence (spoj 2319)

题目大意

给定所有K 位二进制数:0,1,…,2K-1。你需要将它们分成恰好 M组,每组都是原序列中连续的一些数。设Si(1 ≤ i ≤ M)表示第 i组中所有数的二进制表示中 1 的个数,S等于所有Si中的最大值。你的任务是令S最小。

数据规模:1 ≤ K ≤ 100, 1 ≤ M ≤ 100, M ≤ 2K

 

分析

最大值最小,很明显的二分。

首先二分答案,然后贪心的求出最多可以分为几组。

剩下的问题就是怎么样贪心了,我们可以每次选一个点为起点然后再找到一个最大的终点使得所有数1的个数≤S。

 

5.Tickets (sgu 390)

题目大意:
有一位售票员给乘客售票。对于每位乘客,他会卖出多张连续的票,直到已卖出的票的编号的数位之和不小于给定的正数k。 然后他会按照相同的规则给下一位乘客售票。 初始时,售票员持有的票的编号是从L到R的连续整数。 请你求出, 售票员可以售票给多少位乘客。
输入:三个整数L,R和k。
输出:输出一个整数,表示问题的答案。
数据规模:1 ≤ L ≤ R ≤ 1018,1 ≤ k ≤ 1000。

 

分析

本题与上题有些类似,也是对区间内的整数进行连续的分组。与上题不同的是,本题中k 的值较小,也就表示分组数量非常巨大,不可能求出每一个分组。 让我们同之前一样,从特殊情况开始考虑。假设给定的区间是[1..10h-1](也就是一棵完整的高度为h的完全十叉树) ,考虑如何由子问题组合出原问题。这里我们遇到的问题是,两棵子树间的“合并”较难处理:前一棵子树的最后一个区间未必是满的。亦即,一棵子树的头几个元素会并入前一棵子树最后的分组当中。为了解决这个问题,我们引入一维,记录每棵子树之前有多少空间。因此就得到了下面的递推算法:

设f[h,sum,rem]表示对于高度为h的完全十叉树,在这个区间内的每个数需要累加的数字和为sum,在这个区间之前有rem的空间,所能得出的分组数量。f的返回值需要记录两个数:f[h,sum,rem].a记录分组数量,f[h,sum,rem].b 记录其最后一个小组剩余的空间,以便于与下一个子树合并。f可由其十个子树推出:

 

for i:=0 to 9 do f[h,sum,rem]:=merge(f[h,sum,rem],f[h-1,sum+i,f[h,sum,rem].b]);
其中,merge函数表示合并两个记录,它的伪代码是:
function merge(x,y);
   x.a:=x.a+y.a;
   x.b:=y.b; 

 return x;

我们首先求出L和R的最近公共祖先,然后从 L所在的叶子向上走,每走一步就计算所有右侧的兄弟子树 。走到 LCA的下一层后,再向右走到R的祖先。 然后向下走到R所在叶子, 计算途径的左侧兄弟子树。

 

code

2.

 

 

3.

 

 

 

4.

 

 

 

5.

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值