zoukankan      html  css  js  c++  java
  • P3538 [POI2012]OKR-A Horrible Poem

    传送门

    哈希

    首先要知道一个结论:

    判断一个串s中 长度为k的串是不是循环节 的充分必要条件是:

    s[1]~s[len-k] = s[k] ~ s[len] 并且 len%k=0

    怎么证明呢

    如图:

    显然红色的串=s1(因为s[1]~s[len-k] = s[k] ~ s[len])

    同样s1=s2,s2=s3

    显然只要有重复串就一定会形成类似的这种情况

    所以我们就可以利用这点来O(1)判断该串是否为循环节

    但是如果枚举长度k

    时间无法承受

    考虑如何优化

    可以发现 len 一定是 k 的倍数

    可以利用这一点来搞优化

    把 len 从小到大枚举质因数

    那么显然 k 一定是其中几个质因数的乘积

    要如何找出最小的 k 呢

    我们可以把 len 分别除以它的所有质因数

    如果长度为 len/prime_a 的串是循环节

    那么就把 len除以prime_a 然后继续枚举其他质因数

    尝试能否再次减小len

    最后的 len 就是我们要找的 k

    怎么从大到小枚举 len 的质因数也容易

    用欧拉筛的时候我们可以得到每个数的最小质因数(记为nex[ i ])

    只要不断把 nex [ len ] 记录下来然后 len /= nex [ len ] 就行了

    总复杂度约为O(q log n)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef unsigned long long ull;
    const int N=1000007;
    const int base=23333;
    
    int n,m,a[N],l,r;
    int t[N],tot,nex[N];
    
    ull h[N],fac[N];
    char s[N];
    
    int pri[N],cnt;
    bool not_pri[N];
    inline void prenex()//欧拉筛预处理nex
    {
        not_pri[1]=1; nex[1]=1;
        for(int i=2;i<=n;i++)
        {
            if(!not_pri[i])
                pri[++cnt]=i,nex[i]=i;
            for(int j=1;j<=cnt;j++)
            {
                long long g=(long long)i*pri[j];
                if(g>n) break;
                not_pri[g]=1;
                nex[g]=pri[j];
                if(i%pri[j]==0) break;
            }
        }
    }
    inline bool pd(int la,int ra,int lb,int rb)
    //判断串s[la]~s[ra]是否等于s[lb]~s[rb]
    {
        ull h1=h[ra]-h[la-1]*fac[ra-la+1];
        ull h2=h[rb]-h[lb-1]*fac[rb-lb+1];
        return h1==h2;
    }
    int main()
    {
        cin>>n;
        prenex();
        scanf("%s",s+1);
        fac[0]=1;
        for(int i=1;i<=n;i++)
        {
            a[i]=s[i]-'0';
            h[i]=h[i-1]*base+a[i];
            fac[i]=fac[i-1]*base;
        }//预处理哈希值
        cin>>m;
        while(m--)
        {
            scanf("%d%d",&l,&r);
            int len=r-l+1,tot=0;
            while(len!=1)
            {
                t[++tot]=nex[len];
                len/=nex[len];
            }//从小到大找出len的质因数
            len=r-l+1;
            for(int i=1;i<=tot;i++)
            {
                int g=len/t[i];
                if(pd(l,r-g,l+g,r))
                    len=g;
            }//尝试减小len
            printf("%d
    ",len);
        }
        return 0;
    }
  • 相关阅读:
    HDU 2888 Check Corners (模板题)【二维RMQ】
    POJ 3264 Balanced Lineup(模板题)【RMQ】
    poj 3368 Frequent values(经典)【RMQ】
    SPOJ RPLN (模板题)(ST算法)【RMQ】
    UVA 796 Critical Links(模板题)(无向图求桥)
    UVA 315 Network (模板题)(无向图求割点)
    POJ 2029 Get Many Persimmon Trees (模板题)【二维树状数组】
    poj 3067 Japan 【树状数组】
    POJ 2481 Cows 【树状数组】
    POJ 1195 Mobile phones【二维树状数组】
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9640203.html
Copyright © 2011-2022 走看看