题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2795
思路:这其实是一道傻逼题,但数据范围略大了,根本是在欺骗我的感情。
考虑循环节是什么?循环节就是连续重复的子串,设总长度为
L
,循环节长度为
考虑从质因数分解的角度优化它,设
ai<=bi
,若
ai<bi
,很明显a也是
L/pi
的循环节且
L/pi
也是
L
的循环节,而且各个质因子相互独立,逐一处理即可,我们得到的算法是每次除去一个质因子,判断是否是循环节即可,判断循环节可以用
时间复杂度:
O(kQ)
经打表可以得知
k=a1∗p1+a2∗p2+⋅⋅⋅
在
2000
左右,这已经是很优的做法了,题目中的数据范围略大,但没有卡最坏情况,通过自然溢出可以卡过,但我忽略了一个问题就是求字母个数的
GCD
,实际在最坏情况这一步是没有用的,但我跑得好慢啊。
代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#define N 500000
using namespace std;
typedef unsigned long long LL;
LL bin[N + 5],hash[N + 5];
LL n,cnt,prime[N + 5],num[N + 5],f[2][10][N + 5],p,q;
bool not_prime[N + 5];
char s[N + 5];
inline void get_prime(){
memset(not_prime,0,sizeof(not_prime));
cnt = 0;
for (LL i = 2;i <= n; ++i){
if (!not_prime[i]) prime[++cnt] = i;
for (LL j = 1;j <= cnt; ++j)
if (i * prime[j] > n) break;
else {
not_prime[i * prime[j]] = 1;
if (!(i % prime[j])) break;
}
}
}
inline void init(){
scanf("%llu",&n);
scanf("%s",s + 1);
get_prime();
memset(f,0,sizeof(f));
memset(num,0,sizeof(num));
for (LL i = 1;i <= cnt; ++i)
for (LL j = prime[i];j <= n; j += prime[i]){
num[j]++; f[0][num[j]][j] = prime[i];
LL x = j;
while (!(x % prime[i])) { f[1][num[j]][j]++; x /= prime[i]; }
}
p = 37;
bin[0] = 1;
for (LL i = 1;i <= n; ++i) bin[i] = bin[i - 1] * p;
hash[0] = 0;
for (LL i = 1;i <= n; ++i) hash[i] = hash[i - 1] + bin[i - 1] * (s[i] - 96LL);
}
inline LL check(LL ss,LL ll,LL cir){
for (LL i = ss + cir;i < ss + ll;i += cir)
if (!(((hash[i - 1] - hash[i - cir - 1]) * bin[cir]) == (hash[i + cir - 1] - hash[i - 1])))
return 0;
return 1;
}
inline LL get_ans(LL a,LL b){
LL l = b - a + 1,ans;
ans = l;
for (LL i = 1;i <= num[l]; ++i){
for (LL j = f[1][i][l];j; --j)
if (!check(a,ans,ans / f[0][i][l])) break;
else ans /= f[0][i][l];
}
return ans;
}
inline void DO_IT(){
LL Q,a,b;
scanf("%llu",&Q);
while (Q--){
scanf("%llu%llu",&a,&b);
printf("%llu\n",get_ans(a,b));
}
}
int main(){
init();
DO_IT();
return 0;
}
总结:1.当问题不容易做时可以想各种小优化
2.从质因数分解的角度考虑问题
3.相比较自然溢出跑得飞快