bzoj2555 SubString 后缀自动机+LCT

13 篇文章 0 订阅
13 篇文章 0 订阅

Description


要求资瓷

  1. 插入一个字符串
  2. 查询一个字符串出现的次数
    墙制在线,长度 <= 600000,询问次数<= 10000,询问总长度<= 3000000

Solution


拍半天暴力打错了OTZ
据说原本暴力可过???

考虑单次询问怎么做。我们建出模板串的SAM在上面跑,终点的right集合大小显然就是答案
由于这题是动态的,于是我们extend的时候动态维护parent树就行了,直接上LCT维护子树内权值和
好像还可以链修改+单点查询打标记的做法
好像还可以ETT

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define copy(x,t) memcpy(x,t,sizeof(x))

const int N=1200005;

struct treeNode {int son[2],size,siz,val,fa,rev,is_root;} t[N];

int mx[N],fa[N],rec[N][26],last,tot;

char str[N];

void decodeWithMask(char *str,int mask) {
	int len=strlen(str);
	for (int j=0;j<len;j++) {
		mask=(mask*131+j)%len;
		std:: swap(str[j],str[mask]);
	}
}

void push_up(int x) {
	t[x].size=t[t[x].son[0]].size+t[t[x].son[1]].size+t[x].val+t[x].siz;
}

void push_down(int x) {
	if (!t[x].rev) return ;
	t[x].rev=0; std:: swap(t[x].son[0],t[x].son[1]);
	if (t[x].son[0]) t[t[x].son[0]].rev^=1;
	if (t[x].son[1]) t[t[x].son[1]].rev^=1;
}

void rotate(int x) {
	if (t[x].is_root) return ;
	int y=t[x].fa; int z=t[y].fa;
	int k=t[y].son[1]==x;
	t[y].son[k]=t[x].son[!k];
	if (t[x].son[!k]) t[t[x].son[!k]].fa=y;
	t[x].son[!k]=y; t[y].fa=x;
	t[x].fa=z;
	if (t[y].is_root) {
		t[x].is_root=1;
		t[y].is_root=0;
	} else t[z].son[t[z].son[1]==y]=x;
	push_up(y); push_up(x);
}

void remove(int x) {
	if (!t[x].is_root) remove(t[x].fa);
	push_down(x);
}

void splay(int x) {
	remove(x);
	for (;!t[x].is_root;) {
		int y=t[x].fa; int z=t[y].fa;
		if (!t[y].is_root) {
			if ((t[z].son[1]==y)^(t[y].son[1]==x)) rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
}

void access(int x) {
	int y=0;
	while (x) {
		splay(x);
		t[y].is_root=0;
		t[t[x].son[1]].is_root=1;
		t[x].siz+=t[t[x].son[1]].size;
		t[x].son[1]=y;
		t[x].siz-=t[y].size;
		push_up(x);
		y=x; x=t[x].fa;
	}
}

void mroot(int x) {
	access(x);
	splay(x);
	t[x].rev^=1;
}

void link(int x,int y) {
	mroot(x);
	access(y);
	splay(y);
	// mroot(y); 
	t[x].fa=y;
	t[y].siz+=t[x].size;
	push_up(y);
}

void cut(int x,int y) {
	mroot(x); access(y); splay(y);
	t[x].fa=t[y].son[0]=0;
	t[x].is_root=1;
	push_up(y);
}

void extend(int ch) {
	int p,q,np,nq;
	p=last; np=last=++tot; mx[np]=mx[p]+1;
	t[tot].is_root=t[tot].val=1;
	for (;p&&!rec[p][ch];p=fa[p]) rec[p][ch]=np;
	if (!p) {
		fa[np]=1; link(np,1);
	} else {
		q=rec[p][ch];
		if (mx[p]+1==mx[q]) {
			fa[np]=q; link(np,q);
		} else {
			nq=++tot; mx[nq]=mx[p]+1;
			t[tot].is_root=1;
			copy(rec[nq],rec[q]);
			cut(fa[q],q);
			link(nq,fa[q]);
			link(q,nq);
			link(np,nq);
			fa[nq]=fa[q];
			fa[np]=fa[q]=nq;
			for (;p&&rec[p][ch]==q;p=fa[p]) rec[p][ch]=nq;
		}
	}
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int T,mask=0; scanf("%d",&T);
	last=tot=1; t[1].is_root=1;
	scanf("%s",str);
	for (int len=strlen(str),i=0;i<len;++i) {
		extend(str[i]-'A');
	}
	for (;T--;) {
		char opt[8];
		scanf("%s%s",opt,str);
		decodeWithMask(str,mask);
		int len=strlen(str);
		if (opt[0]=='A') {
			for (int i=0;i<len;++i) {
				extend(str[i]-'A');
			}
		} else {
			int now=1,flag=false;
			for (int i=0;i<len;++i) {
				if (rec[now][str[i]-'A']) now=rec[now][str[i]-'A'];
				else {
					flag=true;
					break;
				}
			}
			if (!flag) {
				mroot(1); access(now);
				printf("%d\n", t[now].siz+t[now].val);
				mask^=t[now].siz+t[now].val;
			} else puts("0");
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值