BZOJ 2555 SubString

题解:用LCT维护parent树的Right集合大小

为什么我的代码这么慢???

问题:对SAM理解的还不够深

吐槽:神加密

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1200009;

int T;

int fa[maxn],ch[maxn][2],tag[maxn],v[maxn],rev[maxn];
inline int son(int x){
	if(ch[fa[x]][1]==x)return 1;
	else return 0;
}
inline bool isroot(int x){
	return (ch[fa[x]][0]!=x)&&(ch[fa[x]][1]!=x);
}
inline void pushup(int x){
}
inline void pushdown(int x){
	if(tag[x]){
		tag[ch[x][0]]+=tag[x];
		tag[ch[x][1]]+=tag[x];
		v[ch[x][0]]+=tag[x];
		v[ch[x][1]]+=tag[x];
		tag[x]=0;
	}
	if(rev[x]){
		rev[ch[x][0]]^=1;
		rev[ch[x][1]]^=1;
		rev[x]^=1;
		swap(ch[x][0],ch[x][1]);
	}
}
void Downfa(int x){
	if(!isroot(x))Downfa(fa[x]);
	pushdown(x);
}

inline void Rotate(int x){
	int y=fa[x];
	int z=fa[y];
	int b=son(x),c=son(y);
	int a=ch[x][b^1];
	if(!isroot(y))ch[z][c]=x;
	fa[x]=z;
	if(a)fa[a]=y;
	ch[y][b]=a;
	fa[y]=x;ch[x][b^1]=y;
	pushup(y);pushup(x);
}
void Splay(int x){
	Downfa(x);
	while(!isroot(x)){
		int y=fa[x];
		if(isroot(y)){
			Rotate(x);
		}else{
			if(son(x)==son(y)){
				Rotate(y);Rotate(x);
			}else{
				Rotate(x);Rotate(x);
			}
		}
	}
}

void Access(int x){
	for(int t=0;x;t=x,x=fa[x]){
		Splay(x);ch[x][1]=t;pushup(x);
	}
}
void Makeroot(int x){
	Access(x);Splay(x);rev[x]^=1;
}
void Linkp(int x,int y){
	Makeroot(x);fa[x]=y;
}
void Cutp(int x,int y){
	Makeroot(x);Access(y);Splay(y);
	fa[ch[y][0]]=0;ch[y][0]=0;pushup(y);
}
void Addpath(int x,int y){
	Makeroot(x);Access(y);Splay(y);
	tag[y]+=1;v[y]+=1;
}

int SAMsiz,last,cur;
int ch2[maxn][27];
int dis[maxn];
int par[maxn];
void BuildSAM(int c,int idp){
	cur=++SAMsiz;
	dis[cur]=idp;
	int p=last;last=cur;
	for(;p&&!ch2[p][c];p=par[p])ch2[p][c]=cur;
	if(!p){
		par[cur]=1;
		Linkp(cur,1);
		Addpath(1,cur);
	}else{
		int q=ch2[p][c];
		if(dis[q]==dis[p]+1){
			par[cur]=q;
			Linkp(cur,q);
			Addpath(1,cur);
		}else{
			int nt=++SAMsiz;
			memcpy(ch2[nt],ch2[q],sizeof(ch2[nt]));
			dis[nt]=dis[p]+1;
			Cutp(q,par[q]);
			Linkp(q,nt);
			Linkp(nt,par[q]);
			Linkp(cur,nt);
			Splay(q);Splay(nt);
			v[nt]=v[q];
			Addpath(1,cur);
			par[nt]=par[q];
			par[q]=par[cur]=nt;
			for(;ch2[p][c]==q;p=par[p])ch2[p][c]=nt;
		}
	}
}

int mask,m,nowlen;
char opty[maxn];
char opts[maxn];
void Decode(){
//	return;
	int t=mask;
	for(int i=0;i<m;++i){
		t=(t*131+i)%m;
		swap(opts[i],opts[t]);
	}
}
void Addstring(){
	for(int i=0;i<m;++i)BuildSAM(opts[i]-‘A‘,nowlen+i+1);
	nowlen+=m;
}
int Getans(){
	int x=1;
	for(int i=0;i<m;++i){
		int c=opts[i]-‘A‘;
		x=ch2[x][c];
	}
	if(!x)return 0;
	Splay(x);
	return v[x];
}

int main(){
	scanf("%d",&T);

	SAMsiz=last=1;
	scanf("%s",opts);
	nowlen=m=strlen(opts);
	for(int i=0;i<m;++i)BuildSAM(opts[i]-‘A‘,i+1);

	while(T--){
		scanf("%s%s",opty,opts);
		m=strlen(opts);
		Decode();
		if(opty[0]==‘A‘){
			Addstring();
		}else{
			int ans=Getans();
			printf("%d\n",ans);
			mask^=ans;
		}
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/zzyer/p/8454318.html

时间: 2024-08-30 12:09:58

BZOJ 2555 SubString的相关文章

BZOJ 2555: SubString [后缀自动机 LCT]

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 2045  Solved: 583[Submit][Status][Discuss] Description 懒得写背景了,给你一个字符串init,要求你支持两个操作        (1):在当前字符串的后面插入一个字符串        (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)        你必须在线支持这些操作. Input 第一行一个数Q表示

字符串(LCT,后缀自动机):BZOJ 2555 SubString

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1620  Solved: 471 Description 懒得写背景了,给你一个字符串init,要求你支持两个操作          (1):在当前字符串的后面插入一个字符串          (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)          你必须在线支持这些操作. Input 第一行一个数Q表示操作个数          第二行一个

bzoj 2555 SubString(SAM+LCT)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2555 [题意] 给定一个字符串,可以随时插入字符串,提供查询s在其中作为连续子串的出现次数. [思路] 子串的出现次数,这使我们想到了后缀自动机,如果没有插入操作,则出现次数为字符串对应节点|right|集的大小. Right的递推方法为:|fa->right| <- |right| 因为需要不断地插入字符串,所以parent树会发生变化,我们考虑使用LCT维护parent树.SA

●BZOJ 2555 SubString

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2555题解: 后缀自动机+LCT 不难发现,对于输入的询问串,在自动机里trans后的到的状态的Right集合的大小就是答案. 那么后缀自动机本身就是支持在线添加的,问题就是如何维护好parent树,即如何维护好每个状态的Right集合. 那么Link-Cut-Tree就显然可以完成动态维护parent树的任务. (这里是维护的一颗根固定的树,没有Beroot()等换根函数) 代码: #in

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

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555 建立后缀自动机,就可以直接加入新串了: 出现次数就是 Right 集合的大小,需要查询 Parent 树上的子树和: 所以可以用 LCT 维护 Parent 树,因为 Parent 树是有根树所以不需要 makeroot: 代码中的两种 cut 写法都可以,其实这里的 splay 节点上记的 siz 值不是 splay 子树里的而是原子树( Parent 树上)里的: 注意读入的函数

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

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555 要维护 right 集合的大小.因为 fa 会变,且 fa 构成一棵树,所以考虑用 LCT 来维护-- 和平常写的 LCT 不太一样.因为要的值是原树上子树里的值,所以没有 makeroot ,splay 里不维护 splay 里的子树信息,只维护加法标记,表示 link 一下就给原树的自己到根的那条链上的所有点加了自己的值.cut 就是减掉自己的值.所以 query 或者 spla

BZOJ 2555 SubString 后缀自动机

题目大意:给出一个字符串,支持在线在字符串后面加一个字符串,查询一个字符串在串中出现过几次. 思路:如果不想写正解的话,这个题就是后缀自动机的简单应用.正解其实是LCT+SAM,但是时间比暴力慢一倍... 暴力就很简单了,正序建立后缀自动机,每次查询的时候找到位置直接输出size的值.注意两点,一个是分裂节点的时候,size也要复制过去.查询的时候发现找不到要return 0; CODE: #include <cstdio> #include <cstring> #include

BZOJ 2555 Substring 题解

题意:给你一个字符串init,要求你支持两个操作        (1):在当前字符串的后面插入一个字符串        (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)        必须在线. 构建SAM,再用LCT维护Parent Tree.每次新加一个结点,对它的祖先的Right集合大小都加了1,(Right集合的定义见CLJ的ppt.这里不需要真的存储Right集合,存集合大小即可) 细节比较多,很容易写错.. 1 #include<cstdio> 2 #include&

BZOJ 2555 Substring 后缀自动机+Link-Cut-Tree

题目大意:给定一个初始字符串,提供两种操作: 1.在这个字符串的后面连接一个字符串 2.询问某个字符串在当前串中出现了多少次 SAM大叔的自动机~~ 对于每个询问就是在后缀自动机上找到该子串所对应的节点 找不到返回0 然后这个节点的Right集合的大小就是这个子串的出现次数 每次Extend的时候将新建节点沿着parent指针到根的路径上所有点的Right集合大小+1即可 分裂节点的时候要将Right集合一并复制 这方法虽然暴力但是非常有效 由于parent是一棵树,所以可以用LCT来维护这棵树