bzoj2795: [Poi2012]A Horrible Poem

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2795
思路:这其实是一道傻逼题,但数据范围略大了,根本是在欺骗我的感情。
考虑循环节是什么?循环节就是连续重复的子串,设总长度为 L ,循环节长度为a,一定满足 a|L 那么我们可以暴力枚举 L 的因数暴力判断,但这样会T妥妥的。
考虑从质因数分解的角度优化它,设a=p1a1p2a2 L=p1b1p2b2 ,很明显
ai<=bi ,若 ai<bi ,很明显a也是 L/pi 的循环节且 L/pi 也是 L 的循环节,而且各个质因子相互独立,逐一处理即可,我们得到的算法是每次除去一个质因子,判断是否是循环节即可,判断循环节可以用hash
时间复杂度: O(kQ
经打表可以得知 k=a1p1+a2p2+ 2000 左右,这已经是很优的做法了,题目中的数据范围略大,但没有卡最坏情况,通过自然溢出可以卡过,但我忽略了一个问题就是求字母个数的 GCD ,实际在最坏情况这一步是没有用的,但我跑得好慢啊。

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#define N 500000
using namespace std;
typedef unsigned long long LL;
LL bin[N + 5],hash[N + 5];
LL n,cnt,prime[N + 5],num[N + 5],f[2][10][N + 5],p,q;
bool not_prime[N + 5];
char s[N + 5];
inline void get_prime(){
    memset(not_prime,0,sizeof(not_prime));
    cnt = 0;
    for (LL i = 2;i <= n; ++i){
        if (!not_prime[i]) prime[++cnt] = i;
        for (LL j = 1;j <= cnt; ++j)
          if (i * prime[j] > n) break;
          else {
             not_prime[i * prime[j]] = 1;
             if (!(i % prime[j])) break;
          }
    } 
}

inline  void init(){
    scanf("%llu",&n);
    scanf("%s",s + 1);
    get_prime();
    memset(f,0,sizeof(f));
    memset(num,0,sizeof(num));
    for (LL i = 1;i <= cnt; ++i)
      for (LL j = prime[i];j <= n; j += prime[i]){
          num[j]++; f[0][num[j]][j] = prime[i];
          LL x = j; 
          while (!(x % prime[i])) { f[1][num[j]][j]++; x /= prime[i]; }
      } 
    p = 37; 
    bin[0] = 1;
    for (LL i = 1;i <= n; ++i) bin[i] = bin[i - 1] * p;
    hash[0] = 0;
    for (LL i = 1;i <= n; ++i) hash[i] = hash[i - 1] + bin[i - 1] * (s[i] - 96LL);  
}


inline LL check(LL ss,LL ll,LL cir){
    for (LL i = ss + cir;i < ss + ll;i += cir)
        if (!(((hash[i - 1] - hash[i - cir - 1]) * bin[cir]) == (hash[i + cir - 1] - hash[i - 1])))
         return 0;
    return 1;
}


inline LL get_ans(LL a,LL b){
    LL l = b - a + 1,ans;
    ans = l;
    for (LL i = 1;i <= num[l]; ++i){
        for (LL j = f[1][i][l];j; --j)
          if (!check(a,ans,ans / f[0][i][l])) break;
          else ans /= f[0][i][l];
    }
    return ans;
}

inline void DO_IT(){
    LL Q,a,b;
    scanf("%llu",&Q);
    while (Q--){
        scanf("%llu%llu",&a,&b);
        printf("%llu\n",get_ans(a,b));
    }
} 

int main(){
    init();
    DO_IT();
    return 0;
}

总结:1.当问题不容易做时可以想各种小优化
2.从质因数分解的角度考虑问题
3.相比较自然溢出跑得飞快

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值