bzoj2795: [Poi2012]A Horrible Poem

題目傳送門:http://www.lydsy.com/JudgeOnline/problem.php?id=2795
思路:這其實是一道傻逼題,但數據範圍略大了,根本是在欺騙我的感情。
考慮循環節是什麼?循環節就是連續重複的子串,設總長度爲L ,循環節長度爲a ,一定滿足a|L 那麼我們可以暴力枚舉L 的因數暴力判斷,但這樣會T妥妥的。
考慮從質因數分解的角度優化它,設a=p1a1p2a2L=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.相比較自然溢出跑得飛快

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章