[bzoj3926][Zjoi2015]诸神眷顾的幻想乡
%%%
给定一棵叶子不超过20的树,点有颜色,每两点间的路径构成一个颜色串,所有的串不同的有多少种。
神犇们都太强了!
首先以每个叶子为根,就得到了一个Trie树啊。
Trie树怎么建到SAM上呢,直接以Trie上的父亲节点在SAM中的位置作为$last$,和字符串一样加入建边更新就行了。
怎么把所有20个Trie树建到一个SAM上呢?每个Trie树直接从$init$状态开始建就行了。
这就是广义后缀自动机?为啥对呢,,感觉就非常对2333
#include<bits/stdc++.h> using namespace std; const int N=100010; inline int read(){ int r=0,c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c)) r=r*10+c-'0',c=getchar(); return r; } struct SAM{ int ch[N*20][11],fa[N*20],len[N*20]; int sz; void init(){ sz=0;fa[0]=-1; } int ins(int c,int las){ int now=++sz;len[now]=len[las]+1; int p,q; for(p=las;~p&&!ch[p][c];p=fa[p]) ch[p][c]=now; if(!~p)fa[now]=0; else{ q=ch[p][c]; if(len[q]==len[p]+1) fa[now]=q; else{ int r=++sz; fa[r]=fa[q]; for(int i=0;i<10;i++) ch[r][i]=ch[q][i]; len[r]=len[p]+1; for(;~p&&ch[p][c]==q;p=fa[p]) ch[p][c]=r; fa[q]=fa[now]=r; } } return now; } }atm; struct Edge{ int to,nxt; }e[N*2]; int head[N],cnt=1; int d[N],col[N]; void add(int u,int v){ e[cnt]=(Edge){v,head[u]}; head[u]=cnt++; e[cnt]=(Edge){u,head[v]}; head[v]=cnt++; d[u]++,d[v]++; } void dfs(int u,int fa,int las){ int now=atm.ins(col[u],las); for(int i=head[u];i;i=e[i].nxt) if(e[i].to^fa)dfs(e[i].to,u,now); } int main(){ int n=read();read(); for(int i=1;i<=n;i++) col[i]=read(); for(int i=1;i<n;i++) add(read(),read()); atm.init(); for(int i=1;i<=n;i++) if(d[i]==1)dfs(i,0,0); long long ans=0; for(int i=1;i<=atm.sz;i++) ans+=1ll*(atm.len[i]-atm.len[atm.fa[i]]); cout<<ans; }