【bzoj1014】 JSOI2008—火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 (题目链接)

题意

  给出一个字符串,要求维护这些操作:询问后缀x与后缀y的LCQ(最长公共前缀),在第k个字符后插入一个字符,将第k个字符改成另一个字符。

Solution

  对于修改与插入操作,我们用splay维护。考虑怎么求解LCQ。

  我们可以将字符串hash,然后平衡树上每一个节点的hash值就代表的该区间的hash值,毫无疑问,这是可以向上更新的。于是问题就解决了。

代码

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define MOD 9875321
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=150010;
int tr[maxn][2],fa[maxn],id[maxn],size[maxn];
int v[maxn],h[maxn],bin[maxn];
int n,rt,sz,q;
char s[maxn];

void update(int k) {
	int l=tr[k][0],r=tr[k][1];
	size[k]=size[l]+size[r]+1;
	h[k]=h[l]+(LL)v[k]*bin[size[l]]%MOD+(LL)bin[size[l]+1]*h[r]%MOD;
	h[k]%=MOD;
}
void rotate(int x,int &k) {
	int y=fa[x],z=fa[y],l,r;
	l=tr[y][1]==x;r=l^1;
	if (y==k) k=x;
	else tr[z][tr[z][1]==y]=x;
	fa[x]=z;fa[y]=x;fa[tr[x][r]]=y;
	tr[y][l]=tr[x][r];tr[x][r]=y;
	update(y);update(x);
}
void splay(int x,int &k) {
	while (x!=k) {
		int y=fa[x],z=fa[y];
		if (y!=k) {
			if ((tr[z][0]==y) ^ (tr[y][0]==x)) rotate(x,k);
			else rotate(y,k);
		}
		rotate(x,k);
	}
}
int find(int k,int x) {
	int l=tr[k][0],r=tr[k][1];
	if (size[l]+1==x) return k;
	else if (size[l]>=x) return find(l,x);
	else return find(r,x-size[l]-1);
}
void insert(int k,int val) {
	int x=find(rt,k+1),y=find(rt,k+2);
	splay(x,rt);splay(y,tr[x][1]);
	int z=++sz;tr[y][0]=z;fa[z]=y;v[z]=val;
	update(z);update(y);update(x);
}
int query(int k,int val) {
	int x=find(rt,k),y=find(rt,k+val+1);
	splay(x,rt);splay(y,tr[x][1]);
	return h[tr[y][0]];
}
int solve(int x,int y) {
	int l=1,r=min(sz-x,sz-y)-1,ans=0;
	while (l<=r) {
		int mid=(l+r)>>1;
		if (query(x,mid)==query(y,mid)) l=mid+1,ans=mid;
		else r=mid-1;
	}
	return ans;
}
void build(int l,int r,int f) {
	if (l>r) return;
	int mid=(l+r)>>1,now=id[mid],last=id[f];
	if (l==r) {
		v[now]=h[now]=s[mid]-‘a‘+1;
		fa[now]=last;size[now]=1;
		tr[last][mid>=f]=now;
		return;
	}
	build(l,mid-1,mid);build(mid+1,r,mid);
	v[now]=s[mid]-‘a‘+1;fa[now]=last;update(now);
	tr[last][mid>=f]=now;
}
int main() {
	scanf("%s",s+2);
	n=strlen(s+2);
	bin[0]=1;for (int i=1;i<maxn;i++) bin[i]=bin[i-1]*31%MOD;
	for (int i=1;i<=n+2;i++) id[i]=i;
	build(1,n+2,0);rt=(n+3)>>1;sz=n+2;
	scanf("%d",&q);
	while (q--) {
		int x,y;char ch[10];
		scanf("%s",ch);
		scanf("%d",&x);
		if (ch[0]==‘Q‘) scanf("%d",&y),printf("%d\n",solve(x,y));
		if (ch[0]==‘R‘) {
			scanf("%s",ch);
			x=find(rt,x+1);splay(x,rt);
			v[rt]=ch[0]-‘a‘+1;update(rt);
		}
		if (ch[0]==‘I‘) scanf("%s",ch),insert(x,ch[0]-‘a‘+1);
	}
	return 0;
}

  

时间: 2024-12-25 02:12:22

【bzoj1014】 JSOI2008—火星人prefix的相关文章

bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include<cstdio> #include<cstring> #include<iostream> #define L 100001 typedef unsigned long long ULL; using namespace std; char s[L+4]; int tot,root

[BZOJ1014][JSOI2008]火星人prefix

试题描述 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串,两个字串的公共前缀的长度.比方说,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0

[BZOJ1014] [JSOI2008] 火星人prefix (splay &amp; 二分答案)

Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串,两个字串的公共前缀的长度.比方说,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4,

bzoj1014: [JSOI2008]火星人prefix Hash+Splay

Splay维护Hash值,每次二分. #include<bits/stdc++.h> #define L(t) (t)->c[0] #define R(t) (t)->c[1] #define Z(t) (L(t)->s+1) #define N 100010 #define M (l+r>>1) typedef unsigned long long ull; ull a[N]; struct node{ int v,s; ull u; node* c[2]; n

[BZOJ1014][JSOI2008]火星人prefix splay+hash+二分

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1014 先考虑如果没有修改操作和插入操作,是一个静态的字符串,我们可以怎样快速求得题目中的LCQ. 两个字符串判等很容易想到hash.于是我们二分答案并二分判断,就可以在$log_n$时间内得到答案. 现在加上修改和插入操作,其实就是要动态维护子串也就是一段区间的hash值,这种问题很容易就想到用splay来维护. 每个节点记录此节点管辖下子树的hash值h,当前节点的h=左孩子的h+节点

bzoj1014: [JSOI2008]火星人prefix(splay+hash+二分)

题目大意:一个字符串三个操作:①求两个后缀的LCP②插入一个字符③修改一个字符. 前几天刚学了hash+二分求lcp,就看到这题. 原来splay还能这么用?!原来splay模板这么好写?我以前写的splay是假的吧woc splay每个节点代表一个字符,并维护这个子树代表一个子串的哈希值.因为splay旋转不破坏树结构,所以不论怎么旋转这棵splay树都能代表这个字符串. 预处理处理少了调了半天呜呜呜 赶紧跑去更新自己的splay模板 #include<iostream> #include&

BZOJ-1014 [JSOI2008]火星人prefix (Splay+二分+hash)

题解: 用splay维护添加修改操作,然后二分hash判断长度. 操作一:对于查询区间[l,r]的hash值,显然将l-1旋到根,将r+1旋到根的右儿子,此时所求区间就是根的右儿子的左儿子了. 操作二:将要修改的位置旋到根,然后直接改就可以了. 操作三:要在x后面添加一个字符,显然将x旋到根,x+1旋到根的右儿子,然后直接加在根的右儿子的左儿子上就可以了. 1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio&g

【bzoj1014】[JSOI2008]火星人prefix

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 6031  Solved: 1917[Submit][Status][Discuss] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d

【BZOJ-1014】火星人prefix Splay + 二分 + Hash

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5852  Solved: 1871[Submit][Status][Discuss] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d

JSOI2008 火星人prefix

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2918  Solved: 866[Submit][Status] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人