您的位置:首页 > 其它

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

2017-01-02 20:02 387 查看
题目大意:N个节点的树,每个节点有一种颜色,问存在多少种颜色序列不同的路径。

1<=n<=100000,颜色种类<=10,叶子节点最多有10个

广义后缀自动机是把多个串建到同一个后缀自动机中,该自动机可以识别所有串的所有后缀。以一棵trie树为例,广义后缀自动机在分叉处保存当前的last指针,并在一条支路结束后将last指针调回分叉处保存的指针,然后再进入另一支路。

广义后缀自动机的构建要注意:新加入一个字符x,可能当前节点已经有能转移向x的指针。此时若那个节点的深度刚好为当前节点+1,则那个节点就是我们本应该新建的节点,则本次不新建节点。如果深度不为当前节点+1,则仍然需要分叉,此时新建节点并正常处理即可。

对于本题,由于叶子节点<=10个,显然任何一条路径都可以看做某两个叶子节点之间路径的一部分。所以从每个叶子节点开始进行一次DFS,得到的广义后缀自动机就能表示所有的路径,又由于parent树上每个节点都是一个本质相同的字符串集合,所以节点个数(不包含根)就是本质不同的串数量。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#define gm 100001
using namespace std;
typedef unsigned long long cnt_t;
int n,c;
struct node
{
node *s[10],*f;
size_t dpt;
bool used;
node(size_t dpt=0):s(),f(),dpt(dpt),used(0){}
inline void copy(const node* x){f=x->f,memcpy(s,x->s,sizeof s);}
inline void* operator new(size_t)
{
static node *s,*t;
if(s==t) s=(node*)malloc(sizeof(node)<<16),t=s+(1<<16);
return s++;
}
cnt_t size()
{
cnt_t res=dpt-f->dpt;
used=1;
for(int i=0;i<c;++i)
if(s[i]&&!s[i]->used)
res+=s[i]->size();
return res;
}
};
struct sam
{
node *rt,*last;
sam():rt(new node),last(rt){}
inline void load(){last=rt;}
inline void push_back(int x)
{
node *p=last;
if(p->s[x]&&p->dpt+1==p->s[x]->dpt){last=p->s[x];return;}
node *np=last=new node(p->dpt+1);
while(p&&!p->s[x]) p->s[x]=np,p=p->f;
if(!p) {np->f=rt;return;}
node *q=p->s[x];
if(p->dpt+1==q->dpt) {np->f=q;return;}
node *nq=new node(p->dpt+1);
nq->copy(q);
np->f=nq,q->f=nq;
while(p&&p->s[x]==q) p->s[x]=nq,p=p->f;
}
inline void operator >> (node*& x)
{
x=last;
}
inline void operator << (node* x)
{
last=x;
}
inline cnt_t size()
{
cnt_t res=0;
for(int i=0;i<c;++i)
if(rt->s[i]&&!rt->s[i]->used) res+=rt->s[i]->size();
return res;
}
}tr;
int col[gm],rd[gm];
struct e
{
int t;
e *n;
e(int t,e *n):t(t),n(n){}
}*f[gm];
void dfs(int x,int from=0)
{
node *cache;
tr.push_back(col[x]);
tr>>cache;
for(e *i=f[x];i;i=i->n)
if(i->t!=from)
dfs(i->t,x),tr<<cache;
}
int main()
{
scanf("%d%d",&n,&c);
for(int i=1;i<=n;++i)
scanf("%d",col+i);
for(int i=1,u,v;i<n;++i)
{
scanf("%d%d",&u,&v);
f[u]=new e(v,f[u]);
f[v]=new e(u,f[v]);
++rd[u],++rd[v];
}
for(int i=1;i<=n;++i)
if(rd[i]==1) tr.load(),dfs(i);
printf("%llu\n",tr.size());
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: