【BZOJ3926】【ZJOI2015】诸神眷顾的幻想乡 - 广义后缀自动机
题意:
Description
幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日。
Input
第一行两个正整数n,c。表示空地数量和颜色数量。
Output
一行,输出一个整数,表示答案。
HINT
对于所有数据,1<=n<=100000, 1<=c<=10。
对于15%的数据,n<=2000。
另有5%的数据,所有空地都至多与两个空地相邻。
另有5%的数据,除一块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻。
另有5%的数据,除某两块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻。
题解:
Orz clj 陈老师神题(可能是神(du)仙(liu)ZJOI的开端?)
首先要注意到数据的一个关键性质:叶子节点不会超过20个,所以肯定要将题目关于叶子节点做一些转化;
题目中的树显然可以看成一棵trie,把$A$到$B$路径上的点连起来就能得到一个字符串;
考虑一个在trie树上“转弯”的路径,即从$u$经过$lca(u,v)$再到$v$的一条路径,假如把整棵树以$u$或者$v$的某棵子树中的叶子节点作为根重构的话,就会变成一条深度严格递增的“直”路径,当然原本在trie树上就是“直”的路径也一样;
综合起来就是以每个叶子节点为根重构整棵trie,就一定能把原来trie上的每条路径变成一条深度严格递增的路径;
显然这样的路径必定是此时根节点到某个节点路径上字符串的一个后缀;
那么直接对于所有重构出的trie建出广义后缀自动机,要求所有不相同的子串数量,就利用后缀自动机基本常识统计答案$max[s]-max[fa[s]]$即可;
这里实现广义后缀自动机就在trie上dfs,然后每次把last设为父节点在广义后缀自动机上对应的节点即可(这样做复杂度不是严格线性的,具体见lyy2015国家集训队论文,但是出题人没卡……)
代码:
没能1A差评
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #include<queue>
7 #define inf 2147483647
8 #define eps 1e-9
9 using namespace std;
10 typedef long long ll;
11 typedef double db;
12 struct edge{
13 int v,next;
14 }a[200001];
15 int n,m,u,v,tote=0,rt=1,tot=1,last=1,d[100001],c[100001],head[100001],son[2000001][11],fa[2000001],mx[2000001];
16 ll ans=0;
17 void add(int u,int v){
18 a[++tote].v=v;
19 a[tote].next=head[u];
20 head[u]=tote;
21 }
22 int extend(int p,int ch){
23 int np=++tot;
24 mx[np]=mx[p]+1;
25 for(;p&&!son[p][ch];p=fa[p])son[p][ch]=np;
26 if(!p)fa[np]=rt;
27 else{
28 int q=son[p][ch];
29 if(mx[q]==mx[p]+1)fa[np]=q;
30 else{
31 int nq=++tot;
32 mx[nq]=mx[p]+1;
33 memcpy(son[nq],son[q],sizeof(son[q]));
34 fa[nq]=fa[q];
35 fa[q]=fa[np]=nq;
36 for(;p&&son[p][ch]==q;p=fa[p])son[p][ch]=nq;
37 }
38 }
39 return last=np;
40 }
41 void dfs(int u,int fa,int p){
42 int nw=extend(p,c[u]);
43 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
44 int v=a[tmp].v;
45 if(v!=fa){
46 dfs(v,u,nw);
47 }
48 }
49 }
50 int main(){
51 memset(head,-1,sizeof(head));
52 memset(d,0,sizeof(d));
53 scanf("%d%d",&n,&m);
54 for(int i=1;i<=n;i++){
55 scanf("%d",&c[i]);
56 }
57 for(int i=1;i<n;i++){
58 scanf("%d%d",&u,&v);
59 add(u,v);
60 add(v,u);
61 d[u]++,d[v]++;
62 }
63 for(int i=1;i<=n;i++){
64 if(d[i]==1){
65 dfs(i,-1,rt);
66 }
67 }
68 for(int i=2;i<=tot;i++){
69 ans+=(mx[i]-mx[fa[i]]);
70 }
71 printf("%lld",ans);
72 return 0;
73 }