「一本通 2.1 练习 4」A Horrible Poem

[POI]2012 OKR-A Horrible Poem

思路

​ 看许多题解都是质因数分解,我来一个不一样的

  • 首先暴力分是很好得滴,直接暴力枚举合法的答案即可,但是他跑死了---40分

  • 换个方向,如果 \(S\)\(l-r\) 的循环节的话,那么我们有 \(hs[l,r-s] = hs[s,r]\)\(O(1)\)得到答案----60分!!!

  • 可他还是会 \(T\) ,我们再将其进行优化:

    有数学和规律发现:字串 \(len\) 和每个字母个数的总公约数为循环节的个数
    显然公约数越大,循环节的数量也就越多,对应的答案区间也就越小
    得到公约数后,从头枚举公约数就好了,可是他还是会 \(T\) ---------------------80分!!

    正解 从头到尾可能很长,我们不妨用两头跑的方法,枚举公约数的约数,在得到对应的约数的商,这样就可以两头枚举了,这样就AC了 --------------------100分!!!!

注意

  • 如果还是 \(T\) 那就是您做了无用的冗杂处理,这里列出作者的错误
    • 进制太大( \(131\)\(3\) 即可),最后被卡---\(1.01s\) !!!!
    • 无用的 \(strlen\) !!!!

代码

/*
    直接暴力枚举可能性即可,但是他跑死了。 
    27 ——> 47(洛谷60) —— >97(MLE)--->97(TLE,1.01S)---->100 
    区间长度len和每个字母个数的总公约数为循环节的个数
    显然公约数越大,循环节的数量也就越多,对应的答案区间也就越小
    得到公约数后,就可以从大到小枚举区间,然后检查区间是否符合条件,符合条件的就break;
    这里约数和区间的小技巧
    从小枚举最大公约数的约数,换句话说就是将区间从小的开始枚举,在比较的过程中,可以也记录下
    枚举的约数的区间是否满足条件(防止循环节就一个);
    MLE:进制循环大于开的数组空间
    TLE:1.进制太大,进制为3,速度955ms,
         2.卡时间(1.01s),删除(strlen),速度985; 
     
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#define F(i,l,r) for(int i=l;i<=r;i++)
#define FK(x) memset(x,0,sizeof(0))
#define int unsigned long long

using namespace std;
/*-----------------------------*/
const int manx = 5e5 + 10;
//const int mod = 1e9+9;
const int base = 3;

inline int read(){
    char c = getchar();int x = 0,f = 1;
    for(;!isdigit(c);c = getchar())if(c == '-') f = -1;
    for(;isdigit(c);c = getchar()) x = x*10 + (c^48);
    return x * f;
}
int n, p[manx], m, sum[manx], num[500010][26], x, y;
char a[manx];
inline void prepare(){
    p[0] = 1;
    F(i,1,n) p[i] = p[i-1]*base;    
}
inline int apart(int l, int r){
    return (sum[r] - sum[l-1]*p[r-l+1] ) ;
}
inline bool check(int l, int lenth){
    int x1 = apart(x,y-l),x2 = apart(x+l,y);
    if(x1 == x2) return 1;
    return 0;
}
inline int gcd(int x,int y){
    return y ? gcd(y, x%y) : x;
}
signed main(){
   // freopen("d.in","r",stdin);
   // freopen("d.out","w",stdout);
    n = read();
    prepare();
    scanf("%s", a + 1);
    int s = strlen(a+1);
    m = read();
    F(i,1,s) sum[i] = (sum[i-1]*base + a[i]) ;
    F(i,1,s)
    F(j,1,26){
        num[i][j] = num[i-1][j] + (a[i]-'a'+1==j);
    }
    while(m--){
        x = read(), y = read();
        int ans = 0, l = 1, gcd_;
        int k = y - x + 1;
        gcd_ = k;
        F(i,1,26) gcd_= gcd(gcd_, num[y][i] - num[x-1][i]);
        for(int i = 1; i * i<= gcd_; i++){
            if(gcd_% i == 0){
                if(check(k/(gcd_/i), k)){ans = max(ans, gcd_/i);break;} 
                if(check(k/i, k)) ans = max(ans,i);
            }
        }
        printf("%d\n",k/ans);
    }
    return 0;
}
posted @ 2021-01-10 13:47  zxsoul  阅读(136)  评论(0编辑  收藏  举报