英雄会第二届在线编程大赛·线上初赛:AB数

22 篇文章 0 订阅

http://hero.pongo.cn/Question/Details?ID=161&ExamID=156

So,想来现场参加火热决赛直取mini ipad的,请先通过线上初赛题目AB数

给定两个正整数a,b,分别定义两个集合L和R,

集合L:即把1~a,1~b中整数乘积的集合定义为L= {x * y | x,y是整数且1 <= x <=a , 1 <= y <= b};

集合R:1~a,1~b中整数异或的集合定义为集合R= {x ^ y | x,y是整数且1 <= x <=a , 1 <= y <= b},其中^表示异或运算。

现从L中任取一个整数作为A,从R中任取一个整数作为B,如果必要在B的左边补0,使得B达到:“b的位数+1”位(十进制),然后把B接到A的右边,形成的一个十进制数AB。求所有这样形成数的和。

 

输入a,b 1<=a<=30, 1<=b<=10000000

输出所有产生的AB数的和,由于结果比较大,输出对1000000007取余数的结果。

 

例如:a = 2, b = 4

则L= {1 * 1, 1 * 2, 1 * 3, 1 * 4, 2 * 1, 2 * 2, 2 * 3, 2 * 4} =  {1, 2, 3, 4,6, 8}

 R =  {1^1,1^2,1^3,1^4,2^1,2^2,2^3,2^4} =  {0, 1, 2, 3, 5, 6}

相接的时候保证R中的数至少有两位,所以相接后所有的AB数的集合是

{

100,101, 102, 103, 105, 106,

200,201, 202, 203, 205, 206,

300,301, 302, 303, 305, 306,

400,401, 402, 403, 405, 406,

600,601, 602, 603, 605, 606,

800,801, 802, 803, 805, 806

}

输出它们的和:14502

题外话:

这道题卡了好久,提交了18次,最后算是过了,出问题的地方居然是long和int 的溢出问题。其实早就该知道是这个问题,只是一直怀疑自己的算法是不是写错了,调试算法花了很久,结果算法没错,是代码写错了。

算法复杂度30*7000*2*30

这个已经优化到了极限了。

 

思路:

首先可以简单的证明,集合A与B组合出来的数字不存在重复。

证明:假设有重复,即:

a1b1

a2b2

那么b1和b2的位数肯定不等,假设b1的位数大于b2,那么因为b2至少是(b+1)位,所以b1至少是(b+2)位。而a<=30 , 在y<b的情况下 y^a 不可能是b的100倍,也就是说,y和a取异或,使得y增加的增量不会超过两个数量级。因此集合A与B组合出来的数字不存在重复。

 

基于以上结论,那么,cnt[0]和cnt[1]分别表示集合A和B的元素个数,sum[0]表示集合A中所有的数的和(sum[1] 集合B)

那么 题目要求的数 = sum[0]*cnt[1]*base(b+1)+ sum[1]*cnt[0]         base(b+1) 表示“b的位数+1”

现在,问题转化为如何计算两个集合的大小,以及元素和。

首先说集合B吧,相对来说比集合A简单。

假设 a>b 那么 在计算x^y的时候,因为x<=30 所以 x能影响y的比特位是有限的,最多影响y的后六位。

因此,可以产生这样的思路:假设x的二进制表达长度=5 (例如30, x=11110) , 那么把y的后五位都清零,比这个数小的都在集合B中。然后从这个数开始枚举(判断比它大的数是否可以用x^y表示),一直枚举到y的后五位全是1,枚举的代价是30,每次判断的代价是30。总共计算B的复杂度是O(900)。

 

计算集合A,比较复杂,需要用到鸽巢原理。

假设集合Ai是[1,b]区间内所有整数乘以i得到的集合。

集合A=

A1+A2+...+Aa

- A1∩A2 - A1∩A3 - ...- Aa-1∩Aa

+ A1∩A2∩A3+ A1∩A2∩A4...

-...

规律就是加减交替,然后每次集合求交的数量会增加,一直都最后所有的Ai求交。

上面集合A的表达式有 C(a,1)+C(a,2)+...+C(a,a)=2^a 计算复杂度非常大。

但是,这里有一个后门,也是这道题 b<=10000000的原因。

增因为在b<10000000的情况下,当集合Ai求交的数量增加到一定程度时,结果都是空集,

比如假设A1∩A2是空集那么就没必要计算A1∩A2∩A3了,因此就可以剪枝。

 

最后说求集合交集的方法:

假设集合Ai和集合Aj有一个元素z是相同的,即

z=i*b1

z=j*b2

假设ij的最大公约数是g,即

i=g*ii

j=g*jj

那么

z=g*ii*b1

z=g*jj*b2

因为iijj的最大公约数是1,(ij互素),因此b1一定有约数jjb2一定有约数ii,即

b1=jj*t1

b2=ii*t2

所以,t1=t2,假设t=t1=t2,那么结论是:

z = g*ii*jj*t = (i*j/g)*t

也就是z一定是ij的最小公倍数的整数倍。这样可以通过计算满足条件的t有多少个来计算集合A

由集合A1∩A2 转移到集合A1∩A2∩A3时,可以发现规律是一样的,因此就有转移和递归的思路。

到这一步,还没完,这样做也会超时,需要继续优化。

 

为考虑到A1∩A2转移到A1∩A2∩A2只和最小公倍数有关,因此就可以利用最小公倍数作为递归的参数。而最小公倍数有是有限的,a=30 b=10000000时,也只有7000个不重复的最小公倍数,因此可以想到把递归参数哈希+记忆化。

如此一来求集合A的复杂度只有30*7000*2*30最后一个30是递归函数里面的循环参数。

 

结语:这道题,花了很多时间,最终也没能晋级,不过题目很好。

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值