[BZOJ2795][Poi2012]A Horrible Poem

这题我记得去年福建集训有出过,当时太弱了,完全不懂。现在做到这题才突然想起来可以Hash。

首先无论是Hash还是KMP,要验证循环节,都是用一个方法:如果s[A..A+len-1]s[A..B]的循环节,那么s[A..B-len+1] == s[A+len-1..B]。所以判定的话直接RK Hash就可以搞定了。

但是这题的数据范围有点大,如何破呢?

注意到,如果len是循环节,那么最短循环节必定是len的约数,同时,如果L1L2都是循环节,那么lcm(L1, L2)必然是循环节!所以我们可以把len质因数分解了,对于每个质因数找出最短的循环节长度,总的最短循环节长度就是它们的积。

我的错误

像我这种沙茶,连这种题都会犯很沙茶的错误:

  • RK Hash之后,h[i]的最高次明显比h[i+k]来的低,但是我想反了……
  • 质因数分解O(sqrtn)竟然会写成O(n)的……

代码

bzoj2795[Poi2012]A Horrible Poem
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <cstdio>
#include <cstring>
#include <algorithm>

const int MAXN = 500001, MAXQ = 2000000;
const unsigned long long P = 131;
struct Link
{
  int prime, times;
  Link *next;
} g[1405799], *header[MAXN];
void AddLink(const int x, const int prime, const int times)
{
  static int LinkSize;
  Link* const node = g+(LinkSize++);
  node->prime = prime;
  node->times = times;
  node->next = header[x];
  header[x] = node;
}
int n, q, primes, prime[MAXN];
unsigned long long h[MAXN], p[MAXN] = {1};
char s[MAXN];
void GetPrimes(const int n)
{
  static bool com[MAXN];
  for (int i = 2; i <= n; ++i)
  {
    if (!com[i]) prime[primes++] = i;
    for (int j = 0; j < primes && i*prime[j] <= n; ++j)
    {
      com[i*prime[j]] = true;
      if (i%prime[j] == 0) break;
    }
  }
}
int main()
{
  scanf("%d%s%d", &n, s+1, &q);
  for (int i = 1; i <= n; ++i) p[i] = p[i-1]*P;
  for (int i = 1; i <= n; ++i) h[i] = h[i-1]*P+s[i]-'a';
  GetPrimes(n);
  for (int i = 2, j, x, cnt; i <= n; ++i)
  {
    for (x = i, j = 0; j < primes && prime[j]*prime[j] <= x; ++j)
    {
      for (cnt = 0; x % prime[j] == 0; x /= prime[j]) ++cnt;
      if (cnt) AddLink(i, prime[j], cnt);
    }
    if (x) AddLink(i, x, 1);
  }
  for (int a, b; q--; )
  {
    scanf("%d%d", &a, &b);
    int ans = b-a+1;
    for (Link *e = header[b-a+1]; e; e = e->next)
    {
      int x = 1;
      for (int i = 0; i < e->times; ++i) x *= e->prime;
      for (; x > 1; x /= e->prime)
      {
        const int l = (b-a+1)/x;
        const unsigned long long tmp1 = h[b-l] - h[a-1]*p[b-a+1-l];
        const unsigned long long tmp2 = h[b] - h[a+l-1]*p[b-a+1-l];
        if (tmp1 == tmp2) { ans /= x; break; }
      }
    }
    printf("%d\n", ans);
  }
}

Comments