B.Bazinga(hdu 5510)
kmp
给你n个字符串,你要找到最大的下标i,使得存在j<i,第i个字符串没有第j个字符串这个子串
数据范围:
n<=500,串的长度<=2000
思路:
这个条件十分的奇怪,暴力的话,就算用上kmp,O(L)时间匹配,复杂度也达到了O(n2*L)。后来又想了自动机后缀数组什么的,都不行,最后发现是维护下标+kmp
一开始让j=0,然后遍历i。对于每个i,将串i和串j进行kmp,如果包含,那么j加1,直到j等于i为止,如果存在不包含的那么这个i就是当前最优。那显然,kmp的次数最多是n次,因此复杂度是O(n*L)
那么这为什么对呢?事实上,这个j的含义和条件一样奇怪,它是第一个未匹配的串的下标。我们知道,只要一出现不包含,就是当前最优了。
我们来看全部包含的情况,这时候串i包含所有前面的串,那这时候的j能告诉我们,i前面有某些串包含了j之前的所有串。然后你会发现,只要i包含了j到i-1的串,那么i就能包含前面所有的串,因此我们才会维护j,而不是遍历j
总结:维护第一个未匹配的串的下标,kmp判断是否包含
D.Pagodas(hdu 5512)
题意:
给你两个集合A,B,一开始集合A有两个数a和b(a≠b),集合B有1到n的所有数,但不包括a和b。然后有两个玩家,每个玩家轮流从集合B中取一个数放到集合A中,去数的原则是必须能在集合A中找到两个数使得它们相加或相减的答案等于这个数。谁不能取谁就输,问最后是先手赢还是后手赢
数据范围:
n<=20000,1<=a,b<=n
思路:
你随便玩一下就会发现,能取的数,必然是gcd(a,b)的倍数(事实上也可以通过广义欧几里得ax+by=c来理解)。
并且这个去数没有先后的关系,因此和nim无关。所以你只需要求n/gcd(a,b)-2(减2是a和b不能算),然后看是奇数还是偶数,奇数就是先手赢,偶数就是后手赢
总结:求gcd,判断奇偶性
F.Frogs(hdu 5514)
gcd,约数,容斥原理
题意:
给你n只青蛙,一个数m,还有每只青蛙跳一次的距离ai。数m表示有m块石头围成一圈,下标从0到m-1。然后每只青蛙都从零开始跳(0,ai,2ai...kai%m),跳无限次。问有青蛙到达过的石头的下标之和
数据范围:
n<=10000,m<=10^9,ai<=10^9
思路:
首先把跳无限次的规律找出来,找几个数代进去发现,某只青蛙能跳的石头就是gcd(ai,m)的倍数,这些下标之和是能O(1)算的,设k=gcd(ai,m),那这只青蛙的贡献就是(m2-mk)/2k
如果m是10^5之内,还可以考虑开个bool数组各种乱搞,现在就只能考虑如果避免某些石头被重复算了,也就是要容斥了
首先,所有的k=gcd(ai,m)都是m的约数,然后我们发现,某两个k1,k2的序列如果有重复部分,重复的序列的开头肯定也是m的约数,那么就相当于,约数k1产生的答案,加上约数k2产生的答案,减去某个约数产生的答案。
这显然告诉我们要维护每个约数产生的序列被算了多少次,然后到最后应该是被青蛙到达过的约数都刚好只被算一次
先O(√n)求约数,然后每个青蛙求gcd,遍历约数看这个约数是否整除gcd,整除表示这个约数被青蛙到达过
接着从小到大扫描每个约数,同时维护数组num,num[i]表示第i个约数产生的序列被算了多少次。对于每个i,如果num[i]不是我们所期待的次数,我们就调整它,同时维护答案,由于调整了这个约数的序列,那么这个约数的倍数假如也是约数的话,那它产生的序列也会相应调整,所以我们要遍历i后面的约数,如果能整除,就对它的num值作相应修改
最后m本身这个因子是不应该被算的,但是否特殊处理它其实是没影响的
如果约数的个数是c,那么总的复杂度就是O(√m+cn+c2),c和m的关系比较难确定,c既没有logm这么小,也没有√m这么大,反正就是不会超时就是了
总结:每只青蛙能跳的石头是gcd(ai,m)的倍数,然后维护每个约数贡献的答案被算了多少次来避免重复计算