bzoj1014: [JSOI2008]火星人prefix

1014: [JSOI2008]火星人prefix

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 6181   Solved: 1974
[ 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 a m 现在,
火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串
,两个字串的公共前缀的长度。比方说,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0 在研究LCQ函数的过程
中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出LCQ函数的值;同样,
如果求出了LCQ函数的值,也可以很快地将该字符串的后缀排好序。 尽管火星人聪明地找到了求取LCQ函数的快速
算法,但不甘心认输的地球人又给火星人出了个难题:在求取LCQ函数的同时,还可以改变字符串本身。具体地说
,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此
复杂的问题中,火星人是否还能够做到很快地求取LCQ函数的值。

Input

  第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。接下来的M行,每行描述一个操作。操
作有3种,如下所示
1、询问。语法:Qxy,x,y均为正整数。功能:计算LCQ(x,y)限制:1<=x,y<=当前字符串长度。
2、修改。语法:Rxd,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字
符串长度。
3、插入:语法:Ixd,x是非负整数,d是字符。功能:在字符串第x个字符之后插入字符d,如果x=0,则在字
符串开头插入。限制:x不超过当前字符串长度

Output

  对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。

Sample Input

madamimadam
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11

Sample Output

5
1
0
2
1

HINT

1、所有字符串自始至终都只有小写字母构成。

2、M<=150,000

3、字符串长度L自始至终都满足L<=100,000

4、询问操作的个数不超过10,000个。

对于第1,2个数据,字符串长度自始至终都不超过1,000

对于第3,4,5个数据,没有插入操作。



题解:

用区间splay去维护字符串数组,那2、3就是基本的插入、修改操作了

询问最长公共前缀就二分+hash来搞,了解hash基本原理的话很容易将其应用在splay上


代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<climits>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define N 2000002
using namespace std;
typedef unsigned long long ll;
const ll sed=27;
int n,m,sz,rt;
char ch[N];int a[N];
int tr[N][2],fa[N],size[N];
ll hash[N],pw[N];int v[N];
inline int read()
{
	int x=0,f=1;char ch;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void pushup(int k)
{
	int l=tr[k][0],r=tr[k][1];
	size[k]=size[l]+size[r]+1;
	hash[k]=(hash[l]*pw[size[r]+1]+v[k]*pw[size[r]]+hash[r]);
}
inline void rotate(int x,int &k)
{
	int y=fa[x],z=fa[y],l,r;
	l=tr[y][1]==x;r=l^1;
	if(k==y)k=x;
	else tr[z][tr[z][1]==y]=x;
	fa[y]=x,fa[x]=z,fa[tr[x][r]]=y;
	tr[y][l]=tr[x][r],tr[x][r]=y;
	pushup(y);pushup(x);
}
inline void splay(int x,int &k)
{
	while(x!=k)
	{
		int y=fa[x],z=fa[y];
		if(y!=k)
		{
			if(tr[y][0]==x^tr[z][0]==y)
				rotate(x,k);
			else rotate(y,k);
		}rotate(x,k);
	}
}
inline int find(int k,int rk)
{
	if(!k)return 0;
	int l=tr[k][0],r=tr[k][1];
	if(size[l]+1==rk)return k;
	else if(size[l]>=rk)return find(l,rk);
	else return find(r,rk-size[l]-1);
}
inline void new_node(int k)
{
	size[k]=1;v[k]=a[k-1];
	hash[k]=v[k];
}
inline int build(int l,int r)
{
	if(l>r)return 0;int mid=(l+r)>>1;
	new_node(mid);
	tr[mid][0]=build(l,mid-1);fa[tr[mid][0]]=mid;
	tr[mid][1]=build(mid+1,r);fa[tr[mid][1]]=mid;
	pushup(mid);return mid;
}

inline int check(int x,int y,int len)
{
	int lx=x+1,ly=y+1;
	int rx=lx+len-1,ry=ly+len-1;
	int a=find(rt,lx-1),b=find(rt,rx+1);
	splay(a,rt);splay(b,tr[a][1]);
	int k=tr[b][0];ll tmp=hash[k];
	a=find(rt,ly-1);b=find(rt,ry+1);
	splay(a,rt);splay(b,tr[a][1]);
	k=tr[b][0];ll txp=hash[k];
	if(tmp==txp)return true;
	else return false;
}/*
inline ll query(int k,int len)
{
	int x=find(rt,k),y=find(rt,k+len+1);
	splay(x,rt);splay(y,tr[rt][1]);
	return hash[tr[y][0]];
}*/
inline int ask(int x,int y)
{
	int siz=size[rt];
	int l=1,r=min(siz-x,siz-y)-1,ret=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(x,y,mid))
			l=mid+1,ret=mid;
		else r=mid-1;
	}
	return ret;
}
inline void insert(int x,int y)
{
	int a=find(rt,x+1),b=find(rt,x+2);
	splay(a,rt);splay(b,tr[a][1]);
	int &k=tr[b][0];k=++sz;
	size[k]=1,fa[k]=b,v[k]=hash[k]=y;
	pushup(b);pushup(a);
}
inline void change(int x,int val)
{
	int a=find(rt,x+1);
	splay(a,rt);v[a]=val;
	pushup(a);
}
/*
abcaab
100
Q 1 5
I 0 a
Q 1 5
*/
int main()
{
//	freopen("1014.in","r",stdin);
//	freopen("1014.out","w",stdout);
	scanf("%s",ch+1);m=read();n=strlen(ch+1);pw[0]=1;
	for(int i=1;i<100100;i++)pw[i]=pw[i-1]*sed;
//	for(int i=1;i<=strlen(ch+1);i++)
	for(int i=1;i<=n;i++) 
		a[i]=ch[i]-'a'+1;
	rt=build(1,n+2);sz=size[rt]+1;
	char sh[10],cc[10];
	for(int i=1;i<=m;i++)
	{
		scanf("%s",sh);
		if(sh[0]=='Q')
		{
			int x=read(),y=read();
			printf("%d\n",ask(x,y));
		}
		else if(sh[0]=='I')
		{
			int x=read();
			scanf("%s",cc);
			insert(x,cc[0]-'a'+1);
		}
		else
		{
			int x=read();
			scanf("%s",cc);
			change(x,cc[0]-'a'+1); 
		}
	}
}


【问题描述】 人类终于登上了火星的土地并且见到了神秘的火星人。人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回答。 火星人用一种非常简单的方式来表示数字——掰手指。火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为1,2,3……。火星人的任意两根手指都能随意交换位置,他们就是通过这方法计数的。 一个火星人用一个人类的手演示了如何用手指计数。如果把五根手指——拇指、食指、中指、无名指和小指分别编号为1,2,3,4和5,当它们按正常顺序排列时,形成了5位数12345,当你交换无名指和小指的位置时,会形成5位数12354,当你把五个手指的顺序完全颠倒时,会形成54321,在所有能够形成的120个5位数中,12345最小,它表示1;12354第二小,它表示2;54321最大,它表示120。下表展示了只有3根手指时能够形成的6个3位数和它们代表的数字: 三进制数 123 132 213 231 312 321 代表的数字 1 2 3 4 5 6 现在你有幸成为了第一个和火星人交流的地球人。一个火星人会让你看他的手指,科学家会告诉你要加上去的很小的数。你的任务是,把火星人用手指表示的数与科学家告诉你的数相加,并根据相加的结果改变火星人手指的排列顺序。输入数据保证这个结果不会超出火星人手指能表示的范围。 【输入文件】 输入文件martian.in包括三行,第一行有一个正整数N,表示火星人手指的数目(1 <= N <= 10000)。第二行是一个正整数M,表示要加上去的小整数(1 <= M <= 100)。下一行是1到N这N个整数的一个排列,用空格隔开,表示火星人手指的排列顺序。 【输出文件】 输出文件martian.out只有一行,这一行含有N个整数,表示改变后的火星人手指的排列顺序。每两个相邻的数中间用一个空格分开,不能有多余的空格。 【样例输入】 5 3 1 2 3 4 5 【样例输出】 1 2 4 5 3 用VC环境写的C语言程序,有详尽注释,一看即懂
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值