P3346 [ZJOI2015]诸神眷顾的幻想乡
FxorG
2021-07-09 19:35:07
## $\text{Solution}$
话说这种通过对树的形态特殊限制最近做了还有[一道](https://www.luogu.com.cn/problem/P3241),以保证暴力做法复杂度。
链怎么做?是不是从上至下以及从下到上类似于 trie 的形式建广义 SAM 求不同子串。或者你思考现在只有一段序列,不同子串的求法,用 SAM 就好,那么正反都要,是不是我正向跑一次,反向跑一次,但这时候就要用到广义 SAM 了,然后这种正向依赖前一个字符的序列做法实际上就是链上依赖 trie 树的上一个字符。?
抽象的,我们只是把每一个前缀插入到 SAM 里面了,那么获取这些前缀的方法,我们可以类似于 trie ,但实际上 dfs 保证依赖关系就好了。
![](https://cdn.luogu.com.cn/upload/image_hosting/bhqik8x4.png)
类比到树上:看图下,摸了几遍发现,假如我们要保证正序和倒序的字符串都能被找到,那么我们必须要从叶子节点出发,仿照链的做法即可。
注意下广义 SAM 的 $pre$ 以及空间问题。
## $\text{Code}$
```cpp
#include <bits/stdc++.h>
#define N (int)(2e6+5)
#define ll long long
using namespace std;
struct edge {
int nex,to;
}e[N];
int head[N],cnt;
int du[N],col[N],fa[N],son[10][N],len[N];
int n,m,tot=1;
int rd() {
int f=1,sum=0; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void add_edge(int x,int y) {
e[++cnt].nex=head[x]; e[cnt].to=y;
head[x]=cnt;
}
int ins(int c,int las) {
if(son[c][las]) {
int pre=las,y=son[c][las];
if(len[pre]+1==len[y]) return y;
int x=++tot; len[x]=len[pre]+1;
fa[x]=fa[y]; fa[y]=x;
for(int i=0;i<10;i++) son[i][x]=son[i][y];
for(;pre&&son[c][pre]==y;pre=fa[pre]) son[c][pre]=x;
return x;
}
int pre=las,x=++tot; len[x]=len[pre]+1;
for(;pre&&!son[c][pre];pre=fa[pre]) son[c][pre]=x;
int y=son[c][pre];
if(!pre) fa[x]=1;
else if(len[pre]+1==len[y]) fa[x]=y;
else {
int p=++tot; len[p]=len[pre]+1;
fa[p]=fa[y]; fa[x]=fa[y]=p;
for(int i=0;i<10;i++) son[i][p]=son[i][y];
for(;pre&&son[c][pre]==y;pre=fa[pre]) son[c][pre]=p;
}
return x;
}
void dfs(int x,int fa,int las) {
las=ins(col[x],las);
for(int i=head[x];i;i=e[i].nex) {
int y=e[i].to;
if(y==fa) continue;
dfs(y,x,las);
}
}
int main() {
int x,y;
n=rd(); m=rd();
for(int i=1;i<=n;i++) col[i]=rd();
for(int i=1;i<n;i++) {
x=rd(); y=rd();
add_edge(x,y); add_edge(y,x);
++du[x]; ++du[y];
}
for(int i=1;i<=n;i++) if(du[i]==1) dfs(i,0,1);
ll ans=0;
for(int i=2;i<=tot;i++) ans+=len[i]-len[fa[i]];
printf("%lld",ans);
return 0;
}
```