【bzoj4199/UOJ#131】品酒大会

  • 后缀自动机重修系列第一题

突然发现我的SAM只会原理,不会应用,于是恶补一发

我们知道对于SAM,它的反串的parent树就是后缀树

于是我们可以轻松的建出后缀树然后在树上做dp即可

注意几点:

(1)后缀树的边权表示字符数,所以节点的深度应该按边权计算
(2)后缀树的节点表示状态,对应多个字符串,其size为right集的大小
(3)要维护子树中点权的最大值、次大值、最小值、次小值,因为点权可能是负的
(4)统计第一问的答案时,不能直接上组合数,因为这样做会有不合法情况

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
71
72
73
74
75
76
#include<bits/stdc++.h>
#define FILE "read"
#define MAXN 600010
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
using namespace std;
typedef long long ll;
const ll INF=(1LL<<62);
struct node{int y,next;ll v;}e[MAXN<<2];
int n,last(1),cnt(1),len,mx[MAXN],parent[MAXN],Link[MAXN],tr[MAXN][27];
ll v[MAXN],size[MAXN],deep[MAXN],f[MAXN][2],g[MAXN][2],ans[MAXN][2];
char ch[MAXN];
inline ll read(){
ll 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 insert(int x,int y,ll v){
e[++len].next=Link[x];Link[x]=len;e[len].y=y;e[len].v=v;
e[++len].next=Link[y];Link[y]=len;e[len].y=x;e[len].v=v;
}
void extend(int x,ll val){
int np=++cnt,p=last; size[np]=1;
mx[np]=mx[p]+1; last=np; f[np][0]=g[np][0]=val;
while(p&&!tr[p][x]) tr[p][x]=np,p=parent[p];
if(!p) parent[np]=1;
else{
int q=tr[p][x];
if(mx[q]==mx[p]+1) parent[np]=q;
else{
int nq=++cnt; mx[nq]=mx[p]+1;
memcpy(tr[nq],tr[q],sizeof(tr[q]));
parent[nq]=parent[q]; parent[q]=parent[np]=nq;
while(p&&tr[p][x]==q) tr[p][x]=nq,p=parent[p];
}
}
}
void dfs(int x,int fa){
for(int i=Link[x];i;i=e[i].next)if(e[i].y!=fa){
deep[e[i].y]=deep[x]+e[i].v;
dfs(e[i].y,x);
ans[deep[x]][0]+=size[x]*size[e[i].y];
size[x]+=size[e[i].y];
for(int j=0;j<=1;++j){
if(f[e[i].y][j]>f[x][0]) f[x][1]=f[x][0],f[x][0]=f[e[i].y][j];
else if(f[e[i].y][j]>f[x][1]) f[x][1]=f[e[i].y][j];
if(g[e[i].y][j]<g[x][0]) g[x][1]=g[x][0],g[x][0]=g[e[i].y][j];
else if(g[e[i].y][j]<g[x][1]) g[x][1]=g[e[i].y][j];
}
}
if(f[x][0]!=-INF&&f[x][1]!=-INF) cmax(ans[deep[x]][1],f[x][0]*f[x][1]);
if(g[x][0]!=INF&&g[x][1]!=INF) cmax(ans[deep[x]][1],g[x][0]*g[x][1]);
}
void pre(){
for(int i=0;i<=MAXN-1;++i)
for(int j=0;j<=1;++j)
f[i][j]=-INF,g[i][j]=INF;
for(int i=0;i<=MAXN-1;++i) ans[i][1]=-INF;
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
n=read(); scanf("%s",ch+1);
reverse(ch+1,ch+n+1); pre();
for(int i=1;i<=n;++i) v[n-i+1]=read();
for(int i=1;i<=n;++i) extend(ch[i]-'a',v[i]);
for(int i=1;i<=cnt;++i) if(parent[i]) insert(i,parent[i],mx[i]-mx[parent[i]]);
dfs(1,0);
for(int i=n-1;i>=0;--i){
ans[i][0]+=ans[i+1][0];
cmax(ans[i][1],ans[i+1][1]);
}
for(int i=0;i<n;++i) printf("%lld %lld\n",ans[i][0],ans[i][1]==-INF?0:ans[i][1]);
return 0;
}
文章目录
,