【BZOJ1014】[JSOI2008]火星人prefix

7 篇文章 0 订阅
5 篇文章 0 订阅

题意:给出一字符串,每次:

1、询问从第x个和第y个字符开始的字符串的lcq

2、修改某字符

3、在某字符后插入一字符(可以在开头)

思路:发现又插入操作之后应该就是平衡树了,所以想splay。。

然后lcq改为判断型问题(二分长度)

每个节点记录一下它和它子树构成字符串的hash值。

每次二分时判断一发就好

感觉代码量好大。。调了好久。。

代码:

#include <bits/stdc++.h>
#define N 200009
#define mod 9875321
#define ll long long
using namespace std;
char str[N],ch[10];
int m,x,y,n,val[N],hash[N],fa[N],root,cnt,p[N];
struct Node
{
	int sz,val,hash;
	int ch[2],pnt;
	Node(){}
}a[2000003];
int Rank_get_num(int rank)
{
	int now=root;
	while (1)
	{
		if (a[a[now].ch[0]].sz+1==rank)
		{
			return now;
			break;
		}
		else
			if (a[a[now].ch[0]].sz+1<rank)
				rank-=a[a[now].ch[0]].sz+1,now=a[now].ch[1];
			else now=a[now].ch[0];
	}
}
void updata(int x)
{
	int ls=a[x].ch[0],rs=a[x].ch[1];
	a[x].sz=a[ls].sz+a[rs].sz+1;
	a[x].hash=(a[ls].hash+(ll)a[x].val*p[a[ls].sz]%mod+(ll)p[a[ls].sz+1]*a[rs].hash%mod)%mod;
}
void rotate(int x,bool d)
{
	int y=a[x].pnt;
	a[y].ch[!d]=a[x].ch[d];
	if (a[x].ch[d]!=0) a[a[x].ch[d]].pnt=y;
	a[x].pnt=a[y].pnt;
	if (a[y].pnt!=0)
	{
		if (y==a[a[y].pnt].ch[d]) a[a[y].pnt].ch[d]=x;
		else a[a[y].pnt].ch[!d]=x;
	}
	a[x].ch[d]=y;
	a[y].pnt=x;
	updata(y);
	updata(x);
}
void splay(int x,int target)
{
	int y;
	while (a[x].pnt!=target)
	{
		y=a[x].pnt;
		if (x==a[y].ch[0])
		{
			if (a[y].pnt!=target&&y==a[a[y].pnt].ch[0]) rotate(y,true);
			rotate(x,true);
		}
		else
		{
 			if (a[y].pnt!=target&&y==a[a[y].pnt].ch[1]) rotate(y,false);
			rotate(x,false);
		}
	}
	if (target==0) root=x;
}
void Insert(int x,int y)
{
	int x1=Rank_get_num(x+1);
	int x2=Rank_get_num(x+2);
	splay(x1,0);
	splay(x2,x1);
	int x3=++cnt;
	a[x2].ch[0]=x3;
	a[x3].pnt=x2;
	a[x3].val=y;
	updata(x3);
	updata(x2);
	updata(x1);
}
void build(int l,int r,int fa)
{
	if (l>r) return;
	if (l==r)
	{
		a[l].hash=a[l].val=str[l]-'a'+1;
		a[l].pnt=fa;
		a[l].sz=1;
		if (l<fa) a[fa].ch[0]=l;
		else a[fa].ch[1]=l;
		return;
	}
	int mid=l+r>>1;
	build(l,mid-1,mid);
	build(mid+1,r,mid);
	a[mid].pnt=fa;
	a[mid].val=str[mid]-'a'+1;
	updata(mid);
	if (mid<fa) a[fa].ch[0]=mid;
	else a[fa].ch[1]=mid;
}
int qry(int x,int len)
{
	int x1=Rank_get_num(x);
	int x2=Rank_get_num(x+len+1);
	splay(x1,0);
	splay(x2,root);
	return a[a[x2].ch[0]].hash;
}
int solve(int x,int y)
{
	int l=1,r=min(cnt-x-1,cnt-y-1),ans=0;
	while (l<=r)
	{
		int mid=l+r>>1;
		if (qry(x,mid)==qry(y,mid)) ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}
int main()
{
	p[0]=1;
	for (int i=1;i<=150004;i++) p[i]=p[i-1]*27%mod;
	scanf("%s",str+2);
	n=strlen(str+2);
	str[n+2]=127;
	build(1,n+2,0);
	root=(3+n)>>1;
	cnt=n+2;
	scanf("%d",&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%s%d",ch+1,&x);
		if (ch[1]=='I') scanf("%s",ch+1),Insert(x,ch[1]-'a'+1);
		else 
			if (ch[1]=='R')
			{
				scanf("%s",ch+1);
				int x1=Rank_get_num(x+1);
				splay(x1,0);
				a[root].val=ch[1]-'a'+1;
				updata(root);
			}
			else
			{
				int y;
				scanf("%d",&y);
				printf("%d\n",solve(x,y));
			}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值