由于要保证 K 最大,势必 L 要取最小,所以根据 Next 函数的定义,有Next[KL]=(K−1)L;
即Next[N]=N−L,所以L=N−Next[N];
但是得出的长度 L 还要保证能被 N 整除,所以如果不能整除说明 L = N,即 K = 1;而如果能整除,那么K=N/(N−Next[N]);
因而我们只要对字符串 S 做一趟 KMP,对其求 Next 数组,剩下的就是上述结论
时间复杂度O(n)
二.Fibonacci 进制(fib):
100分做法:DP
N 很大,先尝试几个小数据。可以发现,每个 Fibonacci 表示的长度,和 Fibonacci 数大小有关(1,2,3,5,8,13,21……),这些值最高位上是 1,后面全是 0,即第一个 Fibonacci 表示长度为 i 的数是fib[i]。因此按照长度对 Fibonacci 表示进行分类,长度为 i 的数有fib[i−1]个,看做是第 i 组。那么第 i 组的总长度len[i]=fib[i−1]∗i。
接下来,看 1 出现的次数。长度为 i 的数的表示中,第 i-1 位肯定是 0。
Sum[i]表示前 i 组的 1 的个数。可以得到如下式子:Sum[i]=sum[i−1]+fib[i−1]+sum[i−2]。第 i 组首位 1 的个数为fib[i−1],i−1位为 0,那么最后的 i-2 位的情况,恰好为 1~i-2 组的所有情况。
题目相当于求 n 个数的和不超过 m 的方案数。 首先如果是恰好等于 m,那么就等价于求方程x1+x2+...+xn=m的解的个数,利用插板法可得到公式:C(n+m−1,m) 现在要求不大于 m 的,相当于对i=0...m对C(n+i−1,i)求和,根据 pascal 递推式可以得到答案为C(n+m,m) 现在就需要求C(n+m,m)modP
这里我们主要要解决如何快速计算n!modP
以及当分母有m!modP的情况
1. 当 n,m 都比较小的时候,同时 P 为比较大的素数时,可以直接利用逆元求解,当 n,m 比较大的时候,见下面两种情况(以下参考魏铭 2011 年国家集训队作业)
typedef long long LL;LL calcfac(LL n){ if (n <P) return fac[n]; LL seg = n / P, rem = n % P; LL ret = power(fac[P - 1], seg, P); //fac[P - 1]重复出现了seg次 ret = ret * fac[rem] % P; //除去seg次fac[P – 1]外,剩下的零头 cnt += n / P; //提出n / P个因子P ret = ret * calcfac(n / P) % P; //递归处理 return ret;}
于是n!modp的计算可在O(logn)的时间内解决。
对于分母中的 n!,方法是相似的。若 a 为正整数,a∗a’=1(modP),那么我们称 a’为 a 的逆元,记作 a-1,并有b/a(modP)=b∗a−1(modP)。这样我们只需要把预处理fac[i]=1∗2∗3∗…∗(i–1)∗imodP更换为inv[i]=1−1∗2−1∗3−1∗…∗(i–1)−1∗i−1modP,其计算方法与分子中的 n!计算相差无几,具体可参考我的代码。求逆元可以使用扩展欧几里得算法。
3. 当 p 为合数时
对于某些测试点,我们发现 P 分解后只有 2 个因子,并且不相同,所以我们可以对这两个因子分别运行算法 2,最后用中国剩余定理合并即可。
评论区