题解 P3346 【[ZJOI2015]诸神眷顾的幻想乡】
(本文已被阉割,原文 广义
传送门:诸神眷顾的幻想乡
【题目描述】
给出一颗叶子结点不超过
【分析】
首先有一个很麻烦的地方是路径可以拐弯(即两端点分别在其
所以要想办法把路径捋直,瞎
一颗无根树上任意一条路径必定可以在以某个叶节点为根时,变成一条从上到下的路径(利于广义
\text{SAM} 的使用)。
注意到题目中说叶节点不超过
暴力枚举每一个叶节点作为根节点遍历整棵树啊!
将一共
注意数组大小和空间限制。
【Code (离线)】
(本题
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#define Re register int
#define LL long long
using namespace std;
const int N=4e6+5,N20=2e6+3,Nn=1e5+3;
int n,m,o,x,y,t,C,du[Nn],co[Nn],head[Nn];LL ans;
struct QAQ{int to,next;}a[Nn<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
int fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
struct Trie{
int O,c[N20],fa[N20],tr[N20][10];
Trie(){O=1;}
inline int insert(Re p,Re ch){//在p后面插入一个ch
if(!tr[p][ch])tr[p][ch]=++O,c[O]=ch,fa[O]=p;
return tr[p][ch];
}
}T1;
struct Suffix_Automaton{
int O,pos[N],link[N],trans[N][10],maxlen[N];queue<int>Q;
Suffix_Automaton(){O=1;}
inline int insert(Re ch,Re last){
Re x,y,z=++O,p=last;maxlen[z]=maxlen[last]+1;
while(p&&!trans[p][ch])trans[p][ch]=z,p=link[p];
if(!p)link[z]=1;
else{
x=trans[p][ch];
if(maxlen[p]+1==maxlen[x])link[z]=x;
else{
y=++O;maxlen[y]=maxlen[p]+1;
for(Re i=0;i<C;++i)trans[y][i]=trans[x][i];
while(p&&trans[p][ch]==x)trans[p][ch]=y,p=link[p];
link[y]=link[x],link[z]=link[x]=y;
}
}
return z;
}
inline void build(){
for(Re i=0;i<C;++i)if(T1.tr[1][i])Q.push(T1.tr[1][i]);
pos[1]=1;
while(!Q.empty()){
Re x=Q.front();Q.pop();
pos[x]=insert(T1.c[x],pos[T1.fa[x]]);
for(Re i=0;i<C;++i)if(T1.tr[x][i])Q.push(T1.tr[x][i]);
}
}
inline void sakura(){
for(Re i=2;i<=O;++i)ans+=maxlen[i]-maxlen[link[i]];
printf("%lld\n",ans);
}
}SAM;
inline void dfs(Re x,Re fa,Re fap){//遍历构造Trie树
Re xp=T1.insert(fap,co[x]);//记录在Trie树上的位置,方便下次直接使用
for(Re i=head[x],to;i;i=a[i].next)
if((to=a[i].to)!=fa)dfs(to,x,xp);
}
int main(){
// freopen("123.txt","r",stdin);
in(n),in(C),m=n-1;
for(Re i=1;i<=n;++i)in(co[i]);
while(m--)in(x),in(y),add(x,y),add(y,x),++du[x],++du[y];
for(Re i=1;i<=n;++i)if(du[i]==1)dfs(i,0,1);//依次把每个叶子节点作为根插入Trie树
SAM.build(),SAM.sakura();
}
【Code (在线)】
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#define Re register int
#define LL long long
using namespace std;
const int N=4e6+5,N20=2e6+3,Nn=1e5+3;
int n,m,o,x,y,t,C,du[Nn],co[Nn],head[Nn];LL ans;
struct QAQ{int to,next;}a[Nn<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
int fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
struct Suffix_Automaton{
int O,link[N],trans[N][10],maxlen[N];
Suffix_Automaton(){O=1;}
inline int insert(Re ch,Re last){
if(trans[last][ch]){
Re p=last,x=trans[p][ch];
if(maxlen[p]+1==maxlen[x])return x;
else{
Re y=++O;maxlen[y]=maxlen[p]+1;
for(Re i=0;i<10;++i)trans[y][i]=trans[x][i];
while(p&&trans[p][ch]==x)trans[p][ch]=y,p=link[p];
link[y]=link[x],link[x]=y;
return y;
}
}
Re z=++O,p=last;maxlen[z]=maxlen[last]+1;
while(p&&!trans[p][ch])trans[p][ch]=z,p=link[p];
if(!p)link[z]=1;
else{
Re x=trans[p][ch];
if(maxlen[p]+1==maxlen[x])link[z]=x;
else{
Re y=++O;maxlen[y]=maxlen[p]+1;
for(Re i=0;i<10;++i)trans[y][i]=trans[x][i];
while(p&&trans[p][ch]==x)trans[p][ch]=y,p=link[p];
link[y]=link[x],link[z]=link[x]=y;
}
}
return z;
}
inline void sakura(){
for(Re i=2;i<=O;++i)ans+=maxlen[i]-maxlen[link[i]];
printf("%lld\n",ans);
}
}SAM;
inline void dfs(Re x,Re fa,Re fap){//遍历在线构造SAM
Re xp=SAM.insert(co[x],fap);//记录x在SAM上的位置,方便下次直接使用
for(Re i=head[x],to;i;i=a[i].next)
if((to=a[i].to)!=fa)dfs(to,x,xp);
}
int main(){
// freopen("123.txt","r",stdin);
in(n),in(C),m=n-1;
for(Re i=1;i<=n;++i)in(co[i]);
while(m--)in(x),in(y),add(x,y),add(y,x),++du[x],++du[y];
for(Re i=1;i<=n;++i)if(du[i]==1)dfs(i,0,1);//依次把每个叶子节点作为根插入Trie树
SAM.sakura();
}