【bzoj4516】生成魔咒

  • 本文为博主原创,未经许可不得装载

我们知道统计本质不同的子串个数是$SAM$的经典应用之一

这道题要求在线统计,且字符集大小为$1e9$,怎么办?

先解决字符集的问题,我们只需要将 tr[][] 数组改成$map$即可

至于统计本质不同的子串数,那就是$\sum max(x)-min(x)+1$

因为$min(x)=max(parent(x))+1$,所以也就是求 $\sum max(x)-max(parent(x))$

在线统计时,我们每新建一个节点$np$,就统计它的答案即可

在做题的时候,我一直有一个疑问,那就是为什么不统计新建的nq节点的答案呢?

新建节点前:

(1) $ans(q)=max(q)-max(parent(q))$

新建$nq$节点后:parent(q)$变成了nq,parent(nq)$变成了parent(q)

(2) $ans(q)=max(q)-max(nq)$

(3) $ans(nq)=max(nq)-max(parent(q))$

$(2)(3)$两式相加,我们发现新建节点后的$ans(q)+ans(nq)$的答案与新建节点前$ans(q)$是相等的

这就完美的解释了为什么不统计$nq$节点的答案

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
#include<bits/stdc++.h>
#define MAXN 200010
#define FILE "read"
using namespace std;
typedef long long ll;
int n,cnt(1),now(1),mx[MAXN],par[MAXN];
map<int,int> tr[MAXN];
ll ans;
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
void extend(int x){
int np=++cnt,p=now;
mx[np]=mx[p]+1; now=np;
while(p&&!tr[p][x]) tr[p][x]=np,p=par[p];
if(!p) par[np]=1;
else{
int q=tr[p][x];
if(mx[q]==mx[p]+1) par[np]=q;
else{
int nq=++cnt; mx[nq]=mx[p]+1;
tr[nq]=tr[q];
par[nq]=par[q]; par[q]=par[np]=nq;
while(tr[p][x]==q&&p) tr[p][x]=nq,p=par[p];
}
}
ans+=mx[np]-mx[par[np]];
printf("%lld\n",ans);
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
n=read();
for(int i=1;i<=n;++i){
int x=read(); extend(x);
}
return 0;
}
文章目录
,