BZOJ2795 [Poi2012]A Horrible Poem
阿新 • • 發佈:2019-01-27
code 枚舉 reg 如果 read rri ref 都是 因數分解
題意
給出一個由小寫英文字母組成的字符串S,再給出q個詢問,要求回答S某個子串的最短循環節。
如果字符串B是字符串A的循環節,那麽A可以由B重復若幹次得到。
正整數n (n<=500,000),表示S的長度。
正整數q (q<=2,000,000),表示詢問個數。
分析
參照ww140142的題解。
首先這問題畫一畫發現它絕對不是什麽數據結構能維護的,因為這東西毫無可並性;
所以如果考慮暴力一些呢?
發現循環節長度一定是區間長度n的約數,可以枚舉n的約數;
驗證利用RKhash的特性,可以O(1)取出一段hash值;
比較[l,r]區間len是否為循環節,和比較[l+len-1,r]和[l,r-len+1]這兩段的hash值是等價的;
時間復雜度為O(q√n),看起來。。並不能過!
繼續優化,現在算法的瓶頸是枚舉約數的部分;
那麽我們利用質因數分解,將n分解為多個質因子的乘積;
如果進行一步線性篩的預處理,這一步可以做到log級!
具體就是將vis[i*pri[j]]=1這一步中再記錄一個i*pri[j]的最小質因子為pri[j],然後分解時每次除這東西;
分解降到了log級,但是對一一枚舉約數沒什麽作用;
這裏又有一個性質,如果存在一個長度為len的循環節,那麽對於滿足klen|n的klen,都是循環節;
這個還是比較顯然的,然後就可以倒著搞,每次用n除一個質因子,然後判斷判斷,復雜度\(O(n \log n)\)就可以過了;
代碼
#include<bits/stdc++.h> #define rg register #define il inline #define co const template<class T>il T read() { rg T data=0; rg int w=1; rg char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') w=-1; ch=getchar(); } while(isdigit(ch)) { data=data*10+ch-'0'; ch=getchar(); } return data*w; } template<class T>il T read(rg T&x) { return x=read<T>(); } typedef unsigned long long ll; co int seed=131,N=5e5+1; char str[N]; ll hash[N],Pow[N]; int pri[N],fp[N],st[N],top,cnt; bool judge(int l,int r,int len) { return hash[r-len]-hash[l-1]*Pow[r-len-l+1]==hash[r]-hash[l+len-1]*Pow[r-l-len+1]; } void init(int n) { Pow[0]=1; for(int i=1;i<=n;++i) { hash[i]=hash[i-1]*seed+str[i]; Pow[i]=Pow[i-1]*seed; } for(int i=2;i<=n;++i) { if(!pri[i]) pri[++cnt]=i,fp[i]=i; for(int j=1;j<=cnt&&i*pri[j]<=n;++j) { pri[i*pri[j]]=1; fp[i*pri[j]]=pri[j]; if(i%pri[j]==0) break; } } } int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); int n,m; read(n),scanf("%s",str+1),read(m); init(n); for(int k=1;k<=m;++k) { int l,r,len; read(l),read(r); len=r-l+1; top=0; while(len!=1) { st[++top]=fp[len]; len/=fp[len]; } len=r-l+1; for(int i=1;i<=top;++i) if(judge(l,r,len/st[i])) len/=st[i]; printf("%d\n",len); } return 0; }
BZOJ2795 [Poi2012]A Horrible Poem