• 题意:P4036
    Q x y:计算LCQ(x,y)就是从x开始的后缀与y开始的后缀的最长公共前缀
    R x d:将第x个改为d
    I x d:在x后面加入d
  • 思路
    如果我们想到了字符串哈希(是一种可加可减,很全面的算法)。
    然后我们又想到了二分,这道题就完了!
    可惜我都没想到!
    然后二分后,判断我们发现串是一个动态序列,于是我们很容易用splay维护区间字符串哈希值(这里用的是自然溢出)

所以一道题的思路再复杂也绕不到哪去,你觉得越想越离谱的时候就要知道自己想的多半是错的

当然我说的是\(O(log_2^2)\)的线段树
如果没有操作三的话,线段树静态维护。
因此先离线,我们把I操作加入的点和原来的点按照所有操作结束后的顺序揉在一起,然后我们从后往前枚举操作,我们用树状数组来维护每一位是否(0/1)当前操作后存在,显然一开始就所有数都存在,遇到“I”操作找到rk=对应点+1--,同时存一下每个点的新编号。
不过最后的问题是线段树中会有空的点。其实不影响以为合并的时候是按\(SZ_rs*sum[ls]+sum[rs]\)根据sz来合并的,然后记得sz也是需要动态添加删除的(我忘了,调了好久)

  • code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef unsigned long long ull;
struct query {int opt,x,y;}Q[N];
struct seg {int l,r;ull sum;}T[N];
struct node {ull sum;int cnt;};
ull pw[N],seed=27;
char s[N];
int lf[N],c[N],n,m,mark[N],sz[N];
int lowbit(int x) {return x&(-x);}
void Update(int x,int w) {for(;x<=n;x+=lowbit(x))c[x]+=w;}
int Ask(int x) {int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
void P_up(int x) {int ls=x<<1,rs=ls|1;T[x].sum=T[ls].sum*pw[sz[rs]]+T[rs].sum;sz[x]=sz[ls]+sz[rs];}
void Build(int x,int l,int r) {
	T[x]=(seg){l,r,0};
	if(l==r) {if(lf[l])sz[x]=1,T[x].sum=s[lf[l]]-'a';return;}
	int mid=(l+r)>>1,ls=x<<1,rs=x<<1|1;
	Build(ls,l,mid),Build(rs,mid+1,r);
	P_up(x);
}
node Hash(int x,int l,int r) {
	if(l<=T[x].l&&T[x].r<=r) {return (node){T[x].sum,sz[x]};}
	ull sum=0;int cnt=0,mid=(T[x].l+T[x].r)>>1,ls=x<<1,rs=ls|1;
	if(l<=mid) {node t=Hash(ls,l,r);sum+=t.sum;cnt+=t.cnt;}
	if(r>mid) {node t=Hash(rs,l,r);sum=sum*pw[t.cnt]+t.sum;cnt+=t.cnt;}
	return (node){sum,cnt};
}

void Change(int x,int p,int d,int ad) {
	if(T[x].l==T[x].r) {T[x].sum=d;sz[x]+=ad;return;}
	int mid=(T[x].l+T[x].r)>>1;
	if(p<=mid) Change(x<<1,p,d,ad);
	else Change(x<<1|1,p,d,ad);
	P_up(x);
}

int _fd(int x) {
	int l=1,r=n,best=n;
	while(l<=r) {
		int mid=(l+r)>>1;
		if(Ask(mid)<x) l=mid+1;
		else {best=mid;r=mid-1;}
	}
	return best;
}
void init() {
	scanf("%s",s+1);n=strlen(s+1);
	scanf("%d",&m);
	for(int i=1;i<=m;i++) {
		char ch[3],d[3];int x,y;
		scanf("%s%d",ch,&x);
		if(ch[0]=='Q') {scanf("%d",&y);Q[i]=(query){0,x,y};}
		else if(ch[0]=='R') {scanf("%s",d);y=d[0]-'a';Q[i]=(query){1,x,y};}
		else {scanf("%s",d);++n;y=d[0]-'a';Q[i]=(query){2,x,y};}
	}
	pw[0]=1;for(int i=1;i<=n;i++)Update(i,1),pw[i]=pw[i-1]*seed;
	for(int i=m;i;i--) {
		if(!Q[i].opt) Q[i].y=_fd(Q[i].y);
		if(Q[i].opt==2) Q[i].x=_fd(Q[i].x+1)-1,Update(Q[i].x+1,-1);
		else Q[i].x=_fd(Q[i].x);
	}
	int lst=0;
	for(int i=1;i<=n;i++) if(Ask(i)>lst) lf[i]=++lst;
//	for(int i=1;i<=n;i++) printf("%d ",lf[i]);puts("");
	Build(1,1,n);
}
void solve() {
	int nn=strlen(s+1);
	for(int i=1;i<=m;i++) {
		if(!Q[i].opt) {
			int x=Q[i].x,rx=Ask(x),y=Q[i].y,ry=Ask(y);if(x>y) swap(x,y),swap(rx,ry);
			int l=1,r=nn-ry+1,best=0;
			while(l<=r) {
				int mid=(l+r)>>1;
				if(Hash(1,x,_fd(rx+mid-1)).sum==Hash(1,y,_fd(ry+mid-1)).sum) best=mid,l=mid+1;
				else r=mid-1;
			}
			printf("%d\n",best);
		}
		else if(Q[i].opt==1) {Change(1,Q[i].x,Q[i].y,0);}
		else {Update(Q[i].x+1,1),Change(1,Q[i].x+1,Q[i].y,1);nn++;}
	}
}
int main() {
	init();
	solve();
	return 0;
}