【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡

Description

幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日。

粉丝们非常热情,自发组织表演了一系列节目给幽香看。幽香当然也非常高兴啦。 
这时幽香发现了一件非常有趣的事情,太阳花田有n块空地。在过去,幽香为了方便,在这n块空地之间修建了n-1条边将它们连通起来。也就是说,这n块空地形成了一个树的结构。 
有n个粉丝们来到了太阳花田上。为了表达对幽香生日的祝贺,他们选择了c中颜色的衣服,每种颜色恰好可以用一个0到c-1之间的整数来表示。并且每个人都站在一个空地上,每个空地上也只有一个人。这样整个太阳花田就花花绿绿了。幽香看到了,感觉也非常开心。 
粉丝们策划的一个节目是这样的,选中两个粉丝A和B(A和B可以相同),然后A所在的空地到B所在的空地的路径上的粉丝依次跳起来(包括端点),幽香就能看到一个长度为A到B之间路径上的所有粉丝的数目(包括A和B)的颜色序列。一开始大家打算让人一两个粉丝(注意:A,B和B,A是不同的,他们形成的序列刚好相反,比如红绿蓝和蓝绿红)都来一次,但是有人指出这样可能会出现一些一模一样的颜色序列,会导致审美疲劳。 
于是他们想要问题,在这个树上,一共有多少可能的不同的颜色序列(子串)幽香可以看到呢? 
太阳花田的结构比较特殊,只与一个空地相邻的空地数量不超过20个。 

Input

第一行两个正整数n,c。表示空地数量和颜色数量。

第二行有n个0到c-1之间,由空格隔开的整数,依次表示第i块空地上的粉丝的衣服颜色。(这里我们按照节点标号从小到大的顺序依次给出每块空地上粉丝的衣服颜色)。 
接下来n-1行,每行两个正整数u,v,表示有一条连接空地u和空地v的边。 

Output

一行,输出一个整数,表示答案。

Sample Input

7 3
0 2 1 2 1 0 0
1 2
3 4
3 5
4 6
5 7
2 5

Sample Output

30

HINT

对于所有数据,1<=n<=100000, 1<=c<=10。

对于15%的数据,n<=2000。 
另有5%的数据,所有空地都至多与两个空地相邻。 
另有5%的数据,除一块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻。
另有5%的数据,除某两块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻

题解:由于叶子数量只有20个,所以可以枚举叶子,以每个叶子为根,这样我们就相当于得到了一堆Trie树。

那么广义后缀自动机,就是在多个串,或者Trie树上建的后缀自动机。与普通SAM的区别就是,每次的last指针指向的是它在Trie树上的父亲 在后缀自动机上对应的节点。不过呢,如果你像普通SAM一样来建广义SAM,可能会多建出一些没用的点,不过由于本人懒,多建一些点就多建吧,反正影响不大。

然后这里问的是不同的子串的个数,答案就是每个点的mx-它的parent的mx。怎么理解呢?我们将parent树拎出来,每个点都可以当做一些子串的结束节点,在每个节点结束的子串数量就是mx,但是这样肯定有重复的,我们要减去之前已经被计算过的子串个数,也就是减去parent的mx就行了。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxm=2000010;
const int maxn=200010;
int n,tot,cnt,last;
int ch[maxm][10],mx[maxm],pre[maxm];
int to[maxn],next[maxn],head[maxn],v[maxn],d[maxn];
long long ans;
int extend(int p,int x)
{
if(ch[p][x]&&mx[ch[p][x]]==mx[p]+1) return ch[p][x]; //这里可以防止多建点
int np=++tot;
mx[np]=mx[p]+1;
for(;p&&!ch[p][x];p=pre[p]) ch[p][x]=np;
if(!p) pre[np]=1;
else
{
int q=ch[p][x];
if(mx[q]==mx[p]+1) pre[np]=q;
else
{
int nq=++tot;
mx[nq]=mx[p]+1,pre[nq]=pre[q],pre[np]=pre[q]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
for(;p&&ch[p][x]==q;p=pre[p]) ch[p][x]=nq;
}
}
return np;
}
void dfs(int x,int fa,int p)
{
int i,np=extend(p,v[x]);
for(i=head[x];i!=-1;i=next[i]) if(to[i]!=fa) dfs(to[i],x,np);
}
void add(int a,int b)
{
to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
int main()
{
int i,a,b;
scanf("%d%d",&n,&a);
for(i=1;i<=n;i++) scanf("%d",&v[i]);
memset(head,-1,sizeof(head));
for(i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
d[a]++,d[b]++;
}
tot=1;
for(i=1;i<=n;i++) if(d[i]==1) dfs(i,0,1);
for(i=1;i<=tot;i++) ans+=mx[i]-mx[pre[i]];
printf("%lld",ans);
return 0;
}

【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机的更多相关文章

  1. bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机模板

    #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #d ...

  2. BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 [广义后缀自动机 Trie]

    3926: [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1124  Solved: 660[Submit][S ...

  3. BZOJ.3926.[ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)

    题目链接 要对多个串同时建立SAM,有两种方法: 1.将所有串拼起来,中间用分隔符隔开,插入字符正常插入即可. 2.在这些串的Trie上建SAM.实际上并不需要建Trie,还是只需要正常插入(因为本来 ...

  4. 洛谷P3346 [ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)

    题意 题目链接 Sol 广义SAM的板子题. 首先叶子节点不超过20,那么可以直接对每个叶子节点为根的子树插入到广义SAM中. 因为所有合法的答案一定是某个叶子节点为根的树上的一条链,因此这样可以统计 ...

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

    https://www.lydsy.com/JudgeOnline/problem.php?id=3926 广义后缀自动机是一种可以处理好多字符串的一种数据结构(不像后缀自动机只有处理一到两种的时候比 ...

  6. BZOJ 3926 [Zjoi2015]诸神眷顾的幻想乡 ——广义后缀自动机

    神奇的性质,叶子节点不超过20个. 然后把这些节点提出来构成一颗新树,那么这些树恰好包含了所有的情况. 所以直接广义后缀自动机. 然后统计本质不同的字符串就很简单显然了. #include <c ...

  7. [ZJOI2015]诸神眷顾的幻想乡 广义后缀自动机_DFS_语文题

    才知道题目中是只有20个叶子节点的意思QAQ.... 这次的广义后缀自动机只是将 last 设为 1, 并重新插入. 相比于正统的写法,比较浪费空间. Code: #include <cstdi ...

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

    因为任何一条路径都可以看做某两个叶子节点之间路径的一部分,然后分别把20个叶节点当作根,把整棵树看作trie树,那么一条路径就能看作是从根到某个点这一条路的后缀,构建SAM就能维护不同子串的个数了. ...

  9. 【BZOJ3926】诸神眷顾的幻想乡(后缀自动机)

    [BZOJ3926]诸神眷顾的幻想乡(后缀自动机) 题面 BZOJ 题解 广义后缀自动机啦 求多个串的不同子串个数? 当然是后缀自动机,最后只要把\(longest-parent.longest\)求 ...

随机推荐

  1. Attribute 和 Parameter 的区别

    Attribute 和 Parameter 的区别 (1)HttpServletRequest类有setAttribute()方法,而没有setParameter()方法 (2)当两个Web组件之间为 ...

  2. 面试——String的比较总结

    public class StringTest { private static String getA() {return "a";} public static void ma ...

  3. zabbix agent监控主机配置

    zabbix agent监控主机配置 环境说明 系统版本    CentOS 7.2 x86_64 软件版本    zabbix 3.0.18   1.监控zabbix服务器端 1.1 安装zabbi ...

  4. div 事件透传

    有些时候,我们会想要把一个 div 层覆盖在另一个 div 层上,要让下层的 div 响应鼠标事件而上层的不响应仅仅只做内容展示. 这种时候,我们就可以用到一个 CSS 属性:pointer-even ...

  5. 01.Hello Node.js

    程序下载:https://files.cnblogs.com/files/xiandedanteng/helloNodejs.rar 关键代码: var http=require('http'); v ...

  6. C5:单例模式 Singleton

    保证一个类仅有一个实例,并提供一个访问它的全局访问点. 应用场景:A.一个无状态的类使用单例,可以节省内存B.全局或配置类(其实这个也是无状态的)C.脚本或程序从运行开始到结束,仅需要一个实例来保证数 ...

  7. JQuery修改对象的属性值

    JQuery修改对象的属性值 用到的便是JQuery提供的attr方法,获取属性值的基本结构为:$(obj).attr("属性名"):修改属性值的结构为:$(obj).attr(& ...

  8. mobx 小结

    1.@observable 是一种让数据的变化可以被观察的方法 //@observable data 注册一个数据,这个数据将会成为一个可mobx监测的数据 2.decorator 修饰器只能修饰 类 ...

  9. ionic2项目创建回顾 及 react-native 报错处理

    ionic2: 1.创建项目: ionic start MyIonic2Project tutorial --v2 (下载 tutorial 模板来初始化项目) ionic start MyIonic ...

  10. hdu4888 多校B 最大流以及最大流唯一推断+输出方案

    题意.给一个矩阵,告诉你每行和.每列和.而且限制所填数不大于k,问矩阵是否唯一. 经典建图不说了.第一次遇到推断最大流唯一性的.学习了:用dfs来推断残网中是否还存在环,若存在,则表明绕这个环走一圈, ...