【bzoj3926- [Zjoi2015]诸神眷顾的幻想乡】广义sam

题意:给定一棵树,每个节点有一个颜色,问树上有多少种子串(定义子串为某两个点上的路径),保证叶子节点数<=20。n<=10^5

题解:

叶子节点小于等于20,考虑将每个叶子节点作为根把树给提起来形成一棵trie,然后定义这棵树的子串为从上到下的一个串(深度从浅到深)。

这样做我们可以发现每个子串必定是某棵trie上的一段直线。统计20棵树的不同子串只需要把它们建到一个自动机上就行了,相当于把20棵trie合并成一棵大的。
对于每个节点x,它贡献的子串数量是max[x]-min[x],又因为min[x]=max[fa]+1,则=max[x]-max[fa],就是step[x]-step[fa];
学会了怎样在sam上插入一颗trie,就直接记录一下父亲在sam上的节点作为p。注意每次都要新开一个点,不然会导致无意义的子串出现。

例如一棵树 (括号内为i颜色)

1(0)

2(1)

3(2)  4(3)

2是1的孩子,3和4都是2的孩子。在以1为根节点的时候插入了这棵trie,在以3为根节点的时候son[root][2]已经存在,如果用它来当现在的点的话就会让一棵trie接在另一棵的末位,导致无意义的子串出现,答案偏大。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 using namespace std;
 6
 7 typedef long long LL;
 8 const int N=20*100010;
 9 int n,c,tot,len,last;
10 int w[N],son[N][15],step[N],pre[N],first[N],cnt[N],id[N];
11 struct node{
12     int x,y,next;
13 }a[2*N];
14
15 void ins(int x,int y)
16 {
17     a[++len].x=x;a[len].y=y;
18     a[len].next=first[x];first[x]=len;
19 }
20
21 int add_node(int x)
22 {
23     step[++tot]=x;
24     return tot;
25 }
26
27 int extend(int p,int ch)
28 {
29     // int np;
30     // if(son[p][ch]) return son[p][ch];
31     // else np=add_node(step[p]+1);
32     int np=add_node(step[p]+1);//debug 每次都要新开一个点
33
34     while(p && !son[p][ch]) son[p][ch]=np,p=pre[p];
35     if(p==0) pre[np]=1;
36     else
37     {
38         int q=son[p][ch];
39         if(step[q]==step[p]+1) pre[np]=q;
40         else
41         {
42             int nq=add_node(step[p]+1);
43             memcpy(son[nq],son[q],sizeof(son[q]));
44             pre[nq]=pre[q];
45             pre[np]=pre[q]=nq;
46             while(son[p][ch]==q) son[p][ch]=nq,p=pre[p];
47         }
48     }
49     last=np;
50     return np;
51 }
52
53 void dfs(int x,int fa,int now)
54 {
55     int nt=extend(now,w[x]);
56     // printf("%d\n",nt);
57     for(int i=first[x];i;i=a[i].next)
58     {
59         int y=a[i].y;
60         if(y!=fa) dfs(y,x,nt);
61     }
62 }
63
64 int main()
65 {
66     freopen("a.in","r",stdin);
67     scanf("%d%d",&n,&c);
68     for(int i=1;i<=n;i++) scanf("%d",&w[i]);
69     tot=0;len=0;
70     memset(son,0,sizeof(son));
71     memset(pre,0,sizeof(pre));
72     memset(cnt,0,sizeof(cnt));
73     memset(first,0,sizeof(first));
74     step[++tot]=0;last=1;
75     for(int i=1;i<n;i++)
76     {
77         int x,y;
78         scanf("%d%d",&x,&y);
79         ins(x,y);ins(y,x);
80         cnt[x]++;cnt[y]++;
81     }
82     // for(int i=1;i<=len;i++) printf("%d -- > %d\n",a[i].x,a[i].y);
83     for(int i=1;i<=n;i++)
84     {
85         if(cnt[i]==1) dfs(i,0,1);
86     }
87     // for(int i=1;i<=tot;i++) printf("%d ",id[i]);printf("\n");
88     LL ans=0;
89     for(int i=1;i<=tot;i++) ans+=(LL)(step[i]-step[pre[i]]);
90     printf("%lld\n",ans);
91     return 0;
92 }
时间: 2024-10-07 00:27:45

【bzoj3926- [Zjoi2015]诸神眷顾的幻想乡】广义sam的相关文章

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

[BZOJ3926][Zjoi2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看.幽香当然也非常高兴啦. 这时幽香发现了一件非常有趣的事情,太阳花田有n块空地.在过去,幽香为了方便,在这n块空地之间修建了n-1条边将它们连通起来.也就是说,这n块空地形成了一个树的结构. 有n个粉丝们来到了太阳花田上.为了表达对幽香生日的祝

BZOJ3926 ZJOI2015 诸神眷顾的幻想乡 后缀自动机+DFS

题意:给定一颗字符树,求树中路径所构成的不同的字符串的数量,其中AB和BA视作不同的字符串 题解: 题目里有这样一句话:太阳花田的结构比较特殊,只与一个空地相邻的空地数量不超过20个. 一共有10W个点,却只有20个叶子……因此树上所有的字串就是以叶子为起点搜索出的所有字串,丽洁姐真的好善良啊- -(无雾) 这样从每个点开始就能跑出来一颗Trie树,对Trie构造广义后缀自动机——每个节点看成是一个根,在后面加字符的时候和普通的SAM一样. 然后在SAM上用DFS统计不同字串的数量即可 #inc

bzoj3926: [Zjoi2015]诸神眷顾的幻想乡

1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #define maxn 100005 7 #define maxl 200005 8 #define maxm 4000005 9 using namespace std; 10 11 typedef long long ll; 12

[BZOJ3926] [ZJOI2015]诸神眷顾的幻想乡|后缀自动机

唔..过去了一个多月才敢动一下一试的题呢..毕竟好神啊好神啊 看右边→就写着Orz yu990601 那不如就从他A掉的T3开始吧... 昨天晚上看了一下SAM..大概还是比较亲切的吧 毕竟去年暑假还是听过的...虽然并没有听懂 但是很开心的是现在再看CLJ的讲稿的时候大部分都能看懂了呢 直接这道题吧...首先这棵树的叶子节点不超过20个 然后一个很神奇的性质就是所有的子串都会在以某个叶子节点为根的树上以一条直线的姿态出现... 这么说不利于解题...也就是必定会作为叶子节点到以它为根的树上的某

bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 对广义后缀自动机的一些理解

先说一下对后缀自动机的理解,主要是对构造过程的理解. 构造中,我们已经得到了前L个字符的后缀自动机,现在我们要得到L+1个字符的后缀自动机,什么需要改变呢? 首先,子串$[0,L+1)$对应的状态不存在,应当建立一个状态来表示这个串,显然,这个状态(np)的right集合是{L+1},max=L+1. 现在新建立了一个状态,我们还有两件事要干:找出能转移到这个状态的状态,建立链接:确定这个状态的min,即找到它在parent树上的父亲. 能转移到$np$的状态显然都是right集合包含L的状态,

bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 后缀自动机在tire树上拓展

题意:有棵树每个点有个颜色(不超过10种),每个节点不超过20个儿子,问你每两点之间的颜色序列不同的有多少种 题解:先建出树,对于每个叶子节点,bfs一遍建在sam上,每次保留当前点在sam上的位置,拓展时用父亲节点在sam上的位置当成last即可.然后统计sam本质不同的字符串有多少个 注:dfs建树复杂度是错的,但是也能过这题 /************************************************************** Problem: 3926 User:

BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机 后缀自动机 字符串

https://www.lydsy.com/JudgeOnline/problem.php?id=3926 广义后缀自动机是一种可以处理好多字符串的一种数据结构(不像后缀自动机只有处理一到两种的时候比较方便). 后缀自动机可以说是一种存子串的缩小点数的trie树,广义后缀自动机就是更改了一下塞点的方式让它可以塞多个子串. 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<

luogu3346 诸神眷顾的幻想乡 (广义SAM)

首先,让每一个叶节点做一次树根的话,每个路径一定至少有一次会变成直上直下的 于是对于每个叶节点作为根产生的20个trie树,把它们建到同一个广义SAM里 建法是对每个trie dfs去建,last就是父亲的那个节点:每次做一个新trie时,last给成root 然后答案就是每个节点表示的长度和 1 #include<bits/stdc++.h> 2 #define pa pair<int,int> 3 #define CLR(a,x) memset(a,x,sizeof(a)) 4

[Zjoi2015]诸神眷顾的幻想乡

[Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1537  Solved: 892 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看.幽香当然也非常高兴啦. 这时幽香发现了一件非常有趣的事情,太阳花田有n块空地.在过去,幽香为了方便,在这n块空地之间修建了n-1条

字符串(广义后缀自动机):BZOJ 3926 [Zjoi2015]诸神眷顾的幻想乡

3926: [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 843  Solved: 510[Submit][Status][Discuss] Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看.幽香当然也非常高兴啦. 这时幽香发现了一件非常有趣的事情,太阳花田有n