【BZOJ4545】DQS的trie

Description

DQS的自家阳台上种着一棵颗粒饱满、颜色纯正的trie。
DQS的trie非常的奇特,它初始有n0个节点,n0-1条边,每条边上有一个字符。并且,它拥有极强的生长力:某个i时刻,某个节点就会新生长出一颗子树,它拥有si个节点且节点之间的边上有一个字符,并且新生长出来的子树也是一个树结构。然而因为是新长出来的,根据生活常识可知si必定不会大于i时刻之前的树的大小。
DQS定义trie的子串为从根节点(1号节点)往下走到所有节点所构成的字符串的所有的后缀。DQS身为一个单身doge,常常取出其中一个子串送给妹子,然而他并不希望送给妹子两个相同的子串,所以他非常关心当前trie的本质不同的子串数目。
DQS有时还会去商店购买子串,若他在商店看上某个子串,他希望得知这个子串是否在自家阳台的trie上已经出现,若出现则出现了多少次。如果出现了,他就可以直接回家取trie上的子串辣!
然而DQS身为一个蒟蒻,看着自家阳台的trie树一天天在长大,他被如此众多的节点弄得眼花缭乱,于是他找到了IOI2016Au的你。他会告诉你自家trie树的成长历程,他希望你能够对于每一次询问都做出正确回复。

Input

第一行输入一个整数id,代表测试点编号。
接下来一行输入一个整数n0,表示初始树的大小。
接下来n0-1行,每行两个整数u,v和一个字符c,表示u号节点和v号节点之间有一条边,边上的字母为c。
接下来输入m表示有m组操作。
对于每一组,第一行输入一个整数opt。
若opt=1,则是一组询问,询问当前trie的本质不同的子串数目是多少。
若opt=2,则后面跟两个整数rt,si,表示以点rt为根向下长出一个子树,大小为si。
接下来si-1行,每行两个整数u,v和一个字符c,表示u号节点和v号节点之间有一条边,边上的字母为c。若长出子树之前当前树的大小是n,则这si-1点的编号分别为n+1,n+2…n+si-1。
若opt=3,则是一组询问,后面输入一个字符串S,询问字符串S在当前trie中的出现次数。 

Output

对于每个opt=1或3,输出一行表示答案。

Sample Input

1
4
1 2 a
1 3 b
2 4 b
6
1
2 2 4
2 5 b
2 6 c
5 7 b
1
3 ab
2 6 3
6 8 a
6 9 b
1

Sample Output

3
7
2
11
【数据范围及提示】
第一个询问,本质不同的子串是 a,b,ab。
第二个询问,本质不同的子串是 a,b,c,ab,ac,bb,abb。
第三个询问,ab出现次数是 2。
第四个询问,本质不同的子串是 a,b,c,ab,ac,ca,cb,bb,abb,aca,acb。
opt=1或3时对原树不做修改,只是询问。
每次opt=2,会增加si-1个节点,因为有一个节点是原树上作为新树的根出现的。
数据中,对于链的部分分,满足端点为根节点,每次新建子树都从尾部插入。
对于全部数据,保证从始至终每条边上的字符均为小写字母’a’或’b’或’c’。
n是最终树的大小,N<=100000,M<=100000,Si<=当前树的大小

题解:本题就是3998和2555的结合体,没做过的直接去做那两题吧。

对于第一问,我们可以动态维护所有点的mx[i]-mx[pre]之和,对于第二问,用LCT维护pre树,并将 插入一棵子树,求一个点的子树大小 变成 修改一条链上的权值,查询一个点的值 即可。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=400010;
typedef long long ll;
int n,m,tot,len,cnt;
int to[maxn],next[maxn],val[maxn],head[maxn],pos[maxn];
ll ans;
char str[maxn];
namespace LCT
{
struct node
{
int ch[2],fa;
ll tag,val;
}s[maxn];
int st[maxn],top;
inline bool isr(int x) {return (x!=s[s[x].fa].ch[0])&&(x!=s[s[x].fa].ch[1]);}
inline void pushdown(int x)
{
if(s[x].tag)
{
if(s[x].ch[0]) s[s[x].ch[0]].val+=s[x].tag,s[s[x].ch[0]].tag+=s[x].tag;
if(s[x].ch[1]) s[s[x].ch[1]].val+=s[x].tag,s[s[x].ch[1]].tag+=s[x].tag;
s[x].tag=0;
}
}
inline void rotate(int x)
{
int y=s[x].fa,z=s[y].fa,d=(x==s[y].ch[1]);
if(!isr(y)) s[z].ch[y==s[z].ch[1]]=x;
s[x].fa=z,s[y].fa=x,s[y].ch[d]=s[x].ch[d^1];
if(s[x].ch[d^1]) s[s[x].ch[d^1]].fa=y;
s[x].ch[d^1]=y;
}
inline void updata(int x)
{
int y=x;
st[top=1]=x;
while(!isr(y)) y=s[y].fa,st[++top]=y;
while(top) pushdown(st[top--]);
}
inline void splay(int x)
{
updata(x);
while(!isr(x))
{
int y=s[x].fa,z=s[y].fa;
if(!isr(y))
{
if((x==s[y].ch[0])^(y==s[z].ch[0])) rotate(x);
else rotate(y);
}
rotate(x);
}
}
inline void access(int x)
{
for(int y=0;x;splay(x),s[x].ch[1]=y,y=x,x=s[x].fa);
}
inline void link(int x,int y)
{
access(x),splay(x),s[y].fa=x,s[x].val+=s[y].val,s[x].tag+=s[y].val;
}
inline void cut(int x)
{
access(x),splay(x),s[s[x].ch[0]].val-=s[x].val,s[s[x].ch[0]].tag-=s[x].val;
s[s[x].ch[0]].fa=0,s[x].ch[0]=0;
}
}
namespace SAM
{
int ch[maxn][26],pre[maxn],mx[maxn];
char str[maxn];
inline int extend(int p,int x)
{
int np=++tot;
mx[np]=mx[p]+1,LCT::s[np].val=1;
for(;p&&!ch[p][x];p=pre[p]) ch[p][x]=np;
if(!p) pre[np]=1,ans+=mx[np],LCT::link(1,np);
else
{
int q=ch[p][x];
if(mx[q]==mx[p]+1) pre[np]=q,ans+=mx[np]-mx[q],LCT::link(q,np);
else
{
int nq=++tot; mx[nq]=mx[p]+1;
ans-=mx[q]-mx[pre[q]];
LCT::cut(q);
pre[nq]=pre[q],pre[np]=pre[q]=nq;
ans+=mx[nq]-mx[pre[nq]]+mx[np]-mx[pre[np]]+mx[q]-mx[pre[q]];
LCT::link(pre[nq],nq),LCT::link(pre[q],q),LCT::link(pre[np],np);
memcpy(ch[nq],ch[q],sizeof(ch[q]));
for(;p&&ch[p][x]==q;p=pre[p]) ch[p][x]=nq;
}
}
return np;
}
inline void query()
{
scanf("%s",str),len=strlen(str);
int i,p=1;
for(i=0;i<len&&p;i++) p=ch[p][str[i]-'a'];
if(!p) puts("0");
else
{
LCT::updata(p);
printf("%lld\n",LCT::s[p].val);
}
}
}
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+(gc^'0'),gc=getchar();
return ret*f;
}
void dfs(int x,int fa)
{
for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa) pos[to[i]]=SAM::extend(pos[x],val[i]),dfs(to[i],x);
head[x]=-1;
}
inline void add(int a,int b,int c)
{
to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
int main()
{
rd(),n=rd();
int i,a,b,c,d,op;
tot=1,pos[1]=1;
memset(head,-1,sizeof(head));
for(i=1;i<n;i++) a=rd(),b=rd(),scanf("%s",str),add(a,b,str[0]-'a'),add(b,a,str[0]-'a');
dfs(1,0);
m=rd();
for(i=1;i<=m;i++)
{
op=rd();
if(op==1) printf("%lld\n",ans);
if(op==2)
{
c=rd(),d=rd();
while(--d) a=rd(),b=rd(),scanf("%s",str),add(a,b,str[0]-'a'),add(b,a,str[0]-'a');
dfs(c,0);
}
if(op==3) SAM::query();
}
return 0;
}//1 4 1 2 a 1 3 b 2 4 b 6 1 2 2 4 2 5 b 2 6 c 5 7 b 1 3 ab 2 6 3 6 8 a 6 9 b 1

【BZOJ4545】DQS的trie 后缀自动机+LCT的更多相关文章

  1. BZOJ4545: DQS的trie

    BZOJ4545: DQS的trie https://lydsy.com/JudgeOnline/problem.php?id=4545 分析: 对trie用dfs建sam复杂度是\(O(n^2)\) ...

  2. BZOJ4545: DQS的trie 广义后缀自动机_LCT

    特别鸣神犇 fcwww 替我调出了无数个错误(没他的话我都快自闭了),祝大佬省选rp++ 板子题,给我写了一天QAQ...... 用 LCT 维护后缀树,暴力更新用 LCT 区间更新链即可 其实,在计 ...

  3. bzoj2555(后缀自动机+LCT)

    题目描述 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 你必须在线支持这些操作. 题解 做法很自然,建出后缀自动机,维护每个节点的right ...

  4. 51nod 1600 Simple KMP【后缀自动机+LCT】【思维好题】*

    Description 对于一个字符串|S|,我们定义fail[i],表示最大的x使得S[1..x]=S[i-x+1..i],满足(x<i) 显然对于一个字符串,如果我们将每个0<=i&l ...

  5. BZOJ2555 SubString 【后缀自动机 + LCT】

    题目 懒得写背景了,给你一个字符串init,要求你支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 你必须在线支持这些操作. 输入 ...

  6. bzoj 2555 SubString —— 后缀自动机+LCT

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555 建立后缀自动机,就可以直接加入新串了: 出现次数就是 Right 集合的大小,需要查询 ...

  7. bzoj 2555: SubString【后缀自动机+LCT】

    一直WA--找了半天错的发现居然是解密那里的mask其实是不能动的--传进去的会变,但是真实的那个不会变-- 然后就是后缀自动机,用LCT维护parent树了--注意不能makeroot,因为自动机的 ...

  8. bzoj 2555: SubString 后缀自动机+LCT

    2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 688  Solved: 235[Submit][Status][Dis ...

  9. BZOJ2555 SubString【后缀自动机+LCT】

    Description 懒得写背景了,给你一个字符串init,要求你支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 你必须在线支 ...

随机推荐

  1. RTMP规范 消息与消息块

    Real Time Messaging Protocol(实时消息传输协议) 应用层协议 RTMP协议中, 基本数据单元称为消息(Message).当RTMP协议在互联网中传输数据的时候,消息会被拆分 ...

  2. d3js把circle和rect连接在一起

    怎么办呢...哎...突然就必须全选中了.... 但是...一不小心想到 在g里面都添加circle和rect 但是根据tpye可以让circle的r为0或者rect的width和height为0,这 ...

  3. 第二百九十八节,python操作redis缓存-Set集合类型,可以理解为不能有重复元素的列表

    python操作redis缓存-Set集合类型,可以理解为不能有重复元素的列表 sadd(name,values)name对应的集合中添加元素 #!/usr/bin/env python # -*- ...

  4. 图像处理之3d算法----2d转3d算法介绍

    http://www.3dov.cn/html/c/37/index.html http://news.ifeng.com/a/20151117/46275220_0.shtml 磁力矩阵 http: ...

  5. 关于HTTP keep-alive的实验(转至 http://my.oschina.net/flashsword/blog/80037)

    前面一篇文章提到,HTTP1.1中持久连接已经是默认配置,除非设置Connection为close,否则默认都会进行持久连接.但是我们知道事实标准跟教科书还是可能会有一定差距的,所以不妨自己尝试一下. ...

  6. vncserver的安装和使用

    环境:RedHat Linux 6企业版.Xwindows:gnome (红帽默认安装的图形界面) 尽管我们可以使用SSH连接远程通过字符界面来操作Linux,但是对于更多熟悉图形人来说是很不方便的, ...

  7. ImportError: No module named Crypto.Cipher

    from Crypto.Cipher import AES 报错: ImportError: No module named Crypto.Cipher 解决方法: pip install pycry ...

  8. 使用PHP生成和获取XML格式数据

    1.php生成xml

  9. [转]anchorPoint 锚点解析

    转自:http://blog.csdn.net/cjopengler/article/details/7045638 anchor point 究竟是怎么回事? 之所以造成不容易理解的是因为我们平时看 ...

  10. Phpcms V9当前栏目及所有二级栏目下内容调用标签

    在二级栏目列表页调用: <!--* 获取子栏目* @param $parentid 父级id* @param $type 栏目类型* @param $self 是否包含本身 0为不包含* @pa ...