洛谷 5112 FZOUTSY

小清新好题!
要 LCP 长度不小于 \(k\)?那不就是前 \(k\) 个字符一样嘛……

把每个后缀的前 \(k\) 个字符哈希值搞出来然后就是问一个区间里有多少对数是相同的。
莫队基本操作啊。

但是注意块大小要开成 \(\frac n{\sqrt m}\),否则复杂度 \(O(n \sqrt n)\) 理论上过不去。
(听说好像也是可以过的)

然后据说某些素数会被定向卡(逃

代码:

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
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3e6;
const int M = 1e5;
const int P = 131;
int n,m,k,block;
char s[N + 5];
unsigned long long pw[N + 5],hs[N + 5],suf[N + 5],ind[N + 5];
int a[N + 5],pos[N + 5],cnt[N + 5],len;
struct s_query
{
int l,r,id;
inline bool operator<(const s_query &o) const
{
return pos[l] ^ pos[o.l] ? pos[l] < pos[o.l] : pos[l] & 1 ? r < o.r : r > o.r;
}
} qry[M + 5];
long long ans[M + 5],cur;
int main()
{
scanf("%d%d%d%s",&n,&m,&k,s + 1),block = max(n / sqrt(m),1.0),hs[n + 1] = pw[0] = 1;
for(register int i = 1;i <= n;++i)
pw[i] = pw[i - 1] * P,pos[i] = (i - 1) / block + 1;
for(register int i = n;i;--i)
hs[i] = hs[i + 1] * P + s[i] - 'a';
n -= k - 1;
for(register int i = 1;i <= n;++i)
suf[i] = ind[i] = hs[i] - hs[i + k] * pw[k];
sort(ind + 1,ind + n + 1),len = unique(ind + 1,ind + n + 1) - ind - 1;
for(register int i = 1;i <= n;++i)
a[i] = lower_bound(ind + 1,ind + n + 1,suf[i]) - ind;
for(register int i = 1;i <= m;++i)
scanf("%d%d",&qry[i].l,&qry[i].r),qry[i].l = min(qry[i].l,n),qry[i].r = min(qry[i].r,n),qry[i].id = i;
sort(qry + 1,qry + m + 1);
for(register int i = 1,l = 1,r = 0;i <= m;++i)
{
for(;r < qry[i].r;cur += cnt[a[++r]]++);
for(;l > qry[i].l;cur += cnt[a[--l]]++);
for(;r > qry[i].r;cur -= --cnt[a[r--]]);
for(;l < qry[i].l;cur -= --cnt[a[l++]]);
ans[qry[i].id] = cur;
}
for(register int i = 1;i <= m;++i)
printf("%lld\n",ans[i]);
}