【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. 用户数据验证的正确姿势之assert

    用户数据验证灰常重要, 不用多说了, 但是实现方法(准确的说是表现形式)有很多人, 如何优雅的完成一个后端验证过滤器是一个值得考量的问题, 我尝试过许多方法, 比如validator.js模块, ex ...

  2. java-过滤器Filter_多个Filter的执行顺序

    http://www.cnblogs.com/HigginCui/p/5772514.html [Filter链] *在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称为一个 ...

  3. 01 Developing Successful Oracle Application

    本章提要-------------------------------本章是概述性章节1. 介绍了了解数据库内部结构对于开发的重要性2. 介绍了如何才能开发好的数据库应用程序------------- ...

  4. 自然语言交流系统 phxnet团队 创新实训 项目博客 (一)

    2D文字聊天界面大致预期实现文字输入.发送消息.接收消息.你可以通过点击按钮让机器人开启聊天模式或者学习模式.又或是进入3D语音聊天界面或者退出. 目背景 (1) 开发动机的形态 随着科技的进步与生活 ...

  5. imx6dl uboot 移植

    新版的BSP引进的设备树的机制,在uboot中还添加了menuconfig的配置菜单. 参考官网的文档进行uboot移植,本文使用的cpu是imx6dl,uboot版本2015.04. 我要添加一个名 ...

  6. 使用ffmpeg获取视频流后如何封装存储成mp4文件

    int main(int argc,char *argv[]) 02 { 03  AVFormatContext *pFormatCtx; 04  int i,videoStream; 05  AVC ...

  7. mac下zephir第一步,安装+hello zephir!

    第一步 安装autoconf re2c automake libtool等依赖,如果没有安装git的记得先安装git brew install autoconf brew install automa ...

  8. js 使用事件委托

    想象一下,如果你有一个无序列表,里面有一堆<li>元素,每一个<li>元素都会在点击的时候触发一个行为.这个时候,你通常会在每一个元素上添加一个事件监听,但是如果当这个元素或者 ...

  9. Dll 使用 PChar 参数的小例子 - 回复 "linximf" 的问题

    本例效果图: Dll 文件: library Lib; uses   SysUtils, Classes; {$R *.res} procedure Test(p: PChar); const   T ...

  10. cesium图形上加载图片

    <!DOCTYPE html> <html> <head> <!-- Use correct character set. --> <meta c ...