BZOJ3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机
2017-01-02 20:02
387 查看
题目大意:N个节点的树,每个节点有一种颜色,问存在多少种颜色序列不同的路径。
1<=n<=100000,颜色种类<=10,叶子节点最多有10个
广义后缀自动机是把多个串建到同一个后缀自动机中,该自动机可以识别所有串的所有后缀。以一棵trie树为例,广义后缀自动机在分叉处保存当前的last指针,并在一条支路结束后将last指针调回分叉处保存的指针,然后再进入另一支路。
广义后缀自动机的构建要注意:新加入一个字符x,可能当前节点已经有能转移向x的指针。此时若那个节点的深度刚好为当前节点+1,则那个节点就是我们本应该新建的节点,则本次不新建节点。如果深度不为当前节点+1,则仍然需要分叉,此时新建节点并正常处理即可。
对于本题,由于叶子节点<=10个,显然任何一条路径都可以看做某两个叶子节点之间路径的一部分。所以从每个叶子节点开始进行一次DFS,得到的广义后缀自动机就能表示所有的路径,又由于parent树上每个节点都是一个本质相同的字符串集合,所以节点个数(不包含根)就是本质不同的串数量。
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; }
相关文章推荐
- BZOJ3926 [Zjoi2015]诸神眷顾的幻想乡 【广义后缀自动机】
- BZOJ3926 [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机
- bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机模板
- bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 对[广义后缀自动机]的一些理解
- bzoj 3926: [Zjoi2015]诸神眷顾的幻想乡(广义后缀自动机)
- 【BZOJ3926】【Zjoi2015】诸神眷顾的幻想乡 广义后缀自动机
- 【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机
- 【ZJOI2015】诸神眷顾的幻想乡(广义后缀自动机)
- BZOJ 3926 && ZJOI 2015 诸神眷顾的幻想乡 (广义后缀自动机)
- BZOJ 3926 [Zjoi2015]诸神眷顾的幻想乡 ——广义后缀自动机
- 【BZOJ3926】诸神眷顾的幻想乡(ZJOI2015)-广义后缀自动机
- bzoj3926 [Zjoi2015]诸神眷顾的幻想乡(广义后缀自动机)
- [BZOJ3926][Zjoi2015]诸神眷顾的幻想乡(广义后缀自动机)
- BZOJ.3926.[ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)
- BZOJ3926:[Zjoi2015]诸神眷顾的幻想乡 (后缀自动机)
- bzoj3926[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机
- 字符串(广义后缀自动机):BZOJ 3926 [Zjoi2015]诸神眷顾的幻想乡
- BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 [广义后缀自动机 Trie]
- 【bzoj3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机
- [BZOJ3926][ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)