zoukankan      html  css  js  c++  java
  • 【BZOJ2555】SubString(后缀自动机+LCT)

    点此看题面

    大致题意: 给你一个初始字符串,让你支持两种操作:在它后面接入一个字符串;询问一个字符串出现次数。(强制在线)

    前言

    毒瘤题。。。

    没怎么写过(LCT)维护子树信息,有个地方是自己yy的,结果挂了调得想死。。。

    后缀自动机

    看到这种字符串题目,显然可以上后缀自动机。

    强制在线接入一个字符串,使用后缀自动机似乎没多大问题。而一个字符串的出现次数,只要找到对应节点,该节点的(Sz)即为答案。

    看着好像一点问题都没有,但是。。。

    我们该如何维护(Sz)呢?

    (LCT)

    我们暂时不管后缀自动机,而是单独去考虑它的(parent)树,则(Sz)其实就是一个子树的大小。

    然后想想我们都对(parent)树干了些什么:加点、删边、连边,这不就是个(LCT)嘛!

    等等,你说(LCT)怎么维护子树信息?可以去看看这道题:【洛谷4219】[BJOI2014] 大融合(题解坑掉懒得去补了,因此给的是题目链接)。

    于是这道题就做完了。

    是的,做完了,代码也就七十几行,只是细节有点多而已。。。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 600000
    using namespace std;
    int n;char op[10],s[N+5];
    class LinkCutTree//LCT
    {
    	private:
    		#define PU(x) (O[x].Sz=O[x].ISz+O[O[x].S[0]].Sz+O[O[x].S[1]].Sz+O[x].T)
    		#define IR(x) (O[O[x].F].S[0]^x&&O[O[x].F].S[1]^x)
    		#define Wh(x) (O[O[x].F].S[1]==x)
    		#define Co(x,y,d) (O[O[x].F=y].S[d]=x)
    		#define PD(x) O[x].R&&(Re(O[x].S[0]),Re(O[x].S[1]),O[x].R=0)
    		#define Re(x) (swap(O[x].S[0],O[x].S[1]),O[x].R^=1)
    		#define MR(x) (Ac(x),S(x),Re(x))
    		int St[N<<1];struct node {int T,Sz,ISz,R,F,S[2];}O[N<<1];
    		I void Ro(RI x)
    		{
    			RI f=O[x].F,p=O[f].F,d=Wh(x);!IR(f)&&(O[p].S[Wh(f)]=x),
    			O[x].F=p,Co(O[x].S[d^1],f,d),Co(f,x,d^1),PU(f),PU(x);
    		}
    		I void S(RI x)
    		{
    			RI f=x,T=0;W(St[++T]=f,!IR(f)) f=O[f].F;W(T) PD(St[T]),--T;
    			W(!IR(x)) f=O[x].F,!IR(f)&&(Ro(Wh(x)^Wh(f)?x:f),0),Ro(x);
    		}
    		I void Ac(RI x)
    		{
    			for(RI y=0;x;x=O[y=x].F) S(x),
    				O[x].ISz+=O[O[x].S[1]].Sz-O[y].Sz,O[x].S[1]=y,PU(x);//注意更新虚儿子信息
    		}
    	public:
    		I void Init(CI x) {O[x].Sz=O[x].T=1;}
    		I void Link(CI x,CI y) {MR(x),MR(y),O[O[y].F=x].ISz+=O[y].Sz,O[x].Sz+=O[y].Sz;}//连边
    		I void Cut(CI x,CI y) {MR(x),Ac(y),S(x),O[x].Sz-=O[y].Sz,O[x].S[1]=O[y].F=0;}//删边
    		I int Qry(CI f,CI x) {return MR(f),Ac(x),S(f),O[x].Sz;}//处理询问
    }LCT;
    class SuffixAutomation//后缀自动机
    {
    	private:
    		int Nt,lst;struct node {int L,F,S[30];}O[N<<1];
    	public:
    		I SuffixAutomation() {Nt=lst=1;}
    		I void Ins(CI x)//插入字符
    		{
    			RI p=lst,now=lst=++Nt;O[now].L=O[p].L+1,LCT.Init(now);
    			W(p&&!O[p].S[x]) O[p].S[x]=now,p=O[p].F;if(!p) return LCT.Link(O[now].F=1,now);
    			RI q=O[p].S[x];if(O[p].L+1==O[q].L) return LCT.Link(O[now].F=q,now);
    			RI k=++Nt;(O[k]=O[q]).L=O[p].L+1,O[now].F=O[q].F=k,
    			LCT.Cut(O[k].F,q),LCT.Link(O[k].F,k),LCT.Link(k,now),LCT.Link(k,q);
    			W(p&&O[p].S[x]==q) O[p].S[x]=k,p=O[p].F;
    		}
    		I int Qry(char *s)//询问
    		{
    			RI p=1;for(RI i=1;i<=n;++i) if(!(p=O[p].S[s[i]&31])) return 0;//找到对应节点
    			return LCT.Qry(O[p].F,p);//LCT上询问Sz
    		}
    }S;
    I void T(char *s,RI p) {for(RI i=1;i<=n;++i) swap(s[i],s[(p=(p*131+i-1)%n)+1]);}//强制在线
    int main()
    {
    	RI Qt,i;for(scanf("%d%s",&Qt,s+1),n=strlen(s+1),i=1;i<=n;++i) S.Ins(s[i]&31);//读入并处理初始串
    	RI t,p=0;W(Qt--)
    	{
    		scanf("%s%s",op,s+1),n=strlen(s+1),T(s,p);
    		if(op[0]=='Q') printf("%d
    ",t=S.Qry(s)),p^=t;else for(i=1;i<=n;++i) S.Ins(s[i]&31);
    	}return 0;
    }
    
  • 相关阅读:
    jquery实现表格文本框淡入更改值后淡出
    硬件抽象层
    第八章读书笔记
    Linux驱动——LED闪烁
    编写Linux驱动与统计单词个数
    在开发板上安装Android
    源代码的下载和编译
    初学Git随笔
    Ubuntu Linux环境下的Android开发环境的配置
    Android系统移植于驱动开发概述
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2555.html
Copyright © 2011-2022 走看看