1. 程式人生 > >BZOJ2795 [Poi2012]A Horrible Poem

BZOJ2795 [Poi2012]A Horrible Poem

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