很优美的题目:)
列出以下性质:
1、循环节一定是长度的约数(废话。。。)
2、如果n是一个循环节,那么k*n也必定是一个循环节(关键所在)
3、n是[l,r]这一段的循环节 的充要条件是 [l,r-n]和[l+n,r]相同(利用这个性质我们在判断是否为循环姐是可以做到O(1))
我们从性质2开始,如果len是循环节,那么最小循环节一定是len的约数——是不是有想法了?
枚举所有的质因子,不断除以原长并保证其仍是循环节,直到不能再小为止。
复杂度O(nlogn)(常数很小)
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int Maxn=500005, MaxP=(1e9)+7; char ch; int n,pow[Maxn],i,j,l,r,len,m; int check[Maxn],prime[Maxn],hash[Maxn],tot; int read(){ char ch=getchar(); int ret=0; while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9') {ret=ret*10+ch-'0'; ch=getchar();} return ret; } void init(){ for (i=2;i<Maxn;i++){ if (check[i]==0) check[i]=i, prime[++tot]=i; for (j=1;j<=tot;j++){ if (i*prime[j]>=Maxn) break; check[i*prime[j]]=prime[j]; if (i%prime[j]==0) break; } } } bool equal(int l1,int r1,int l2,int r2){ int t1=hash[r1]-hash[l1-1]*pow[r1-l1+1]; int t2=hash[r2]-hash[l2-1]*pow[r2-l2+1]; return (t1==t2); } int main(){ freopen("okr.in","r",stdin); freopen("okr.out","w",stdout); init(); scanf("%d\n",&n); for (i=1,pow[0]=1;i<=n;i++) pow[i]=pow[i-1]*MaxP; for (i=1;i<=n;i++){ ch=getchar(); hash[i]=hash[i-1]*MaxP+ch; } m=read(); while (m--){ l=read(); r=read(); len=r-l+1; for (i=len;i>1;){ j=check[i]; while ( len%j==0 && equal(l,r-len/j,l+len/j,r) ) len/=j; while (i%j==0) i=i/j; } printf("%d\n",len); } return 0; }