您的位置:首页 > 理论基础 > 数据结构算法

BZOJ3926 [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机

2017-06-26 22:32 260 查看
神々が恋した幻想郷 ~ Kamigami ga Koishita Gensoukyou

题意:一棵树点上有字符,求树上路径所组成的本质不同的字符串的个数,保证叶子数<=20

Sol:

陈老师出的题..Orz

首先由一个结论:树上任意一条简单路径都可以被以某个叶子为根的树上的一条纵向链表示,即枚举每个叶子作为根,所有纵向链的并集=所有路径的集合

简单证明:随便选一个叶子作根,不是纵向链的简单路径一定能被其他两个叶子作根时表示。

那我们可以枚举每个叶子为根DFS,建出一棵Trie树,求Trie树上本质不同的字符串个数

用广义后缀自动机实现,具体可以参考2015年国家集训队论文或者陈老师的博客

事实上不用存储Trie树的形态,DFS过程中用一个变量维护位置就行

写完这题打通了风神录Hard(虽然还是炸过去的QAQ  

Code:

#include<bits/stdc++.h>
#define PAD(x,y) memset(x,y,sizeof x)
#define CPY(x,y) memcpy(x,y,sizeof x)
using namespace std;
const int N = 100009, M = N * 20;
const int alpha = 10;

int n,m;
struct SAM
{
int tot,root;
struct state
{
int son[alpha];
int mx,par;
}node[M<<1];
void init()
{
tot=root=1;
PAD(node,0);
}
int extend(int p,int x)
{
if(node[p].son[x])
{
int q=node[p].son[x];
if(node[p].mx+1==node[q].mx) return q;
else
{
int nq=++tot;
CPY(node[nq].son,node[q].son);
node[nq].mx=node[p].mx+1;
node[nq].par=node[q].par;
node[q].par=nq;
for(;p&&node[p].son[x]==q;p=node[p].par) node[p].son[x]=nq;
return nq;
}
}
else
{
int np=++tot;
node[np].mx=node[p].mx+1;
for(;p&&node[p].son[x]==0;p=node[p].par) node[p].son[x]=np;
if(p==0) node[np].par=root;
else
{
int q=node[p].son[x];
if(node[p].mx+1==node[q].mx) node[np].par=q;
else
{
int nq=++tot;
CPY(node[nq].son,node[q].son);
node[nq].mx=node[p].mx+1;
node[nq].par=node[q].par;
node[np].par=node[q].par=nq;
for(;p&&node[p].son[x]==q;p=node[p].par) node[p].son[x]=nq;
}
}
return np;
}
}
void solve()
{
long long ans=0;
for(int i=1;i<=tot;i++) ans+=(node[i].mx-node[node[i].par].mx);
printf("%lld\n",ans);
}
}uuz;
int first
;
struct edg
{
int next;
int to;
}e[N<<1];
int e_sum;
int col
,degree
;

inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void add_edg(int x,int y)
{
e_sum++;
e[e_sum].next=first[x];
first[x]=e_sum;
e[e_sum].to=y;
}

void dfs(int x,int fa,int last)
{
int nxt=uuz.extend(last,col[x]);
for(int i=first[x];i;i=e[i].next)
{
int w=e[i].to;
if(w==fa) continue;
dfs(w,x,nxt);
}
}

int main()
{
n=read();m=read();
for(int i=1;i<=n;i++) col[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
degree[x]++;degree[y]++;
add_edg(x,y);add_edg(y,x);
}
uuz.init();
for(int i=1;i<=n;i++) if(degree[i]==1) dfs(i,0,uuz.root);
uuz.solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息