1014: [JSOI2008]火星人prefix
Time Limit: 10 Sec
Memory Limit: 162 MB
Submit: 5420
Solved: 1713
[ 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个数据,没有插入操作。
Source
面对这题我整个人是崩溃的…… splay党,无限TLE。
于是交了一发标程,卡着时限过了。
不过我现在知道怎么略微优化让它跑过了,但是…… 我懒得改了 = =
先贴一发代码先:splay维护字符串,维护区间的hash值。 代码下面再说怎么优化 - -
#include "stdio.h"
#include "iostream"
#define rep(f,a,b) for(f=a;f<=b;f++)
#define Empty null,null,null
using namespace std;
typedef long long ll;
const int L=100005,M=1<<7;
const ll mod=9875321,di=(ll)1e4;
struct node {
node*c[2],*fa; char val; int siz; ll hash;
void ins(ll h,node*c0,node*c1,node*f,char c);
void pushup(); node();
} Tnull,*null=&Tnull,*root;
ll mul[L]; char s[L]; int len,q;
node:: node(){ c[0]=c[1]=fa=null; siz=hash=0; val='\0';}
inline ll mu(ll a,ll b,ll m){
ll c=a%di*b,d=a/di*b;
c%=m; d%=m; d*=di;
return (c+d)%m;
}
void node:: pushup(){
siz=c[0]->siz+c[1]->siz+1;
hash=c[1]->hash+val*mul[c[1]->siz]+mu(c[0]->hash,mul[c[1]->siz+1],mod);
hash%=mod;
}
void node:: ins(ll h,node* c0,node* c1,node* f,char ch){
hash=h; val=ch; c[0]=c0,c[1]=c1,fa=f; siz=1;
}
void read(int&a){ char ch=getchar(); a=0;
while (ch<'0'||ch>'9') ch=getchar();
while (ch<='9'&&ch>='0') a=a*10+ch-'0',ch=getchar();
}
int readin(char *s){ char ch=getchar(); int cnt=0;
while (ch==' '||ch=='\n') ch=getchar();
do s[++cnt]=ch,ch=getchar(); while (ch!=' '&&ch!='\n');
return cnt;
}
void rotate(node*x,node*&k){
node *y=x->fa,*z=y->fa; if(y==k&&y==root) k=x;
int l=y->c[1]==x,r=l^1; x->fa=z,y->fa=x;
if(x->c[r]!=null) x->c[r]->fa=y;
if (z!=null) if(z->c[0]==y) z->c[0]=x; else z->c[1]=x;
y->c[l]=x->c[r]; x->c[r]=y; y->pushup(); x->pushup();
}
void splay(node*x,node*&k){
while(x!=k){
node*y=x->fa,*z=y->fa;
if(y!=k){
if (y->c[0]==x^z->c[0]==y) rotate(x,k);
else rotate(y,k);
}
rotate(x,k);
}
if (x->fa!=null) x->fa->pushup();
}
node*build(char*c,int len){
int mid=(len+1)>>1; node*r=new node;
if(len<=0) return null;
if(len==1) return (r->ins(c[1],Empty,c[1]),r);
r->ins(0,build(c,mid-1),build(c+mid,len-mid),null,c[mid]);
r->pushup(); if(r->c[0]!=null) r->c[0]->fa=r;
r->c[1]->fa=r; return r;
}
node*find(int rank,node*now){
if (rank==now->c[0]->siz) return now;
if (rank<now->c[0]->siz) return find(rank,now->c[0]);
return find(rank-now->c[0]->siz-1,now->c[1]);
}
void init(){ len=readin(s+1); int i; mul[0]=1;
rep(i,1,L-2) mul[i]=mul[i-1]*M%mod;
root=build(s,len+2);
}
void split(int x,node*&to){ node*t=find(x,root); splay(t,to);}
void remove(int x,char c){ split(x,root); root->val=c; root->pushup();}
void insert(int x,char c){
split(x+1,root); split(x,root->c[0]); node*t=new node;
t->ins(0,null,null,root->c[0],c); root->c[0]->c[1]=t;
root->c[0]->c[1]->pushup(); root->c[0]->pushup(); root->pushup();
}
void query(int x,int y){ if(x>y) swap(x,y);
int bot=0,mid,top=root->siz-y-1;
do { mid=(bot+top+1)>>1;
split(x-1,root); split(x+mid,root->c[1]);
ll a=root->c[1]->c[0]->hash;
split(y-1,root); split(y+mid,root->c[1]);
ll b=root->c[1]->c[0]->hash;
if (a==b) bot=mid; else top=mid-1;
} while (bot<top); int ans=bot;
printf("%d\n",ans);
}
void work(){ char c,ch; int x,y,i; read(q);
rep(i,1,q) {
readin((&c)-1);
if(c=='Q'){
read(x),read(y);
query(x,y);
} else {
read(x),readin((&ch)-1);
if(c=='I') insert(x,ch);
else remove(x,ch);
}
}
}
int main(){
// freopen("prefix.in","r",stdin);
// freopen("prefix.out","w",stdout);
init(); work();
return 0;
}
对TLE,我只想说一句话:毒瘤! - -
好吧现在考虑怎么优化。总结了一下我能想到的有以下几点:
1.将long long的大素数改为int的素数(如9875321),虽然冲突概率增加,但是能加快速度。如果真的怕冲突,可以用2个short的素数做双hash
2.在适当的函数前加inline
3.同步建一棵splay,旋转分别在两棵树上进行,询问的常数大致能降一半左右
4.对于二分答案的区间小到一定值,如100时,暴力获取之后的数据进行暴力对比
然而,我实在是懒得去实现了qwq。如果有谁用以上方法将我的程序改进后A了,劳烦评论下,感激不尽- -