[JSOI2008]火星人prefix
[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 在研究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 11OUTPUT
5
1
0
2
1
解题报告
无旋$Treap$+$Hash$+二分答案
用无旋$Treap$或者$Splay$维护$Hash$,查询时二分答案,取区间$Hash$值验证
1 /************************************************************** 2 Problem: 1014 3 User: MAFIA 4 Language: C++ 5 Result: Accepted 6 Time:10612 ms 7 Memory:5628 kb 8 ****************************************************************/ 9 10 #include<iostream> 11 #include<cstring> 12 #include<cstdlib> 13 #include<cstdio> 14 #include<ctime> 15 using namespace std; 16 inline int read(){ 17 int sum(0); 18 char ch(getchar()); 19 while(ch<'0'||ch>'9') 20 ch=getchar(); 21 while(ch>='0'&&ch<='9'){ 22 sum=sum*10+ch-'0'; 23 ch=getchar(); 24 } 25 return sum; 26 } 27 int n,m,top; 28 typedef unsigned long long L; 29 const L p=2333; 30 L xp[100005]; 31 struct node{ 32 int key,size; 33 L has; 34 char c; 35 node *ch[2]; 36 node(char x=0):size(1),key(rand()),c(x),has(x){ 37 ch[0]=ch[1]=NULL; 38 } 39 inline void pushup(); 40 }*root,*st[100005]; 41 typedef pair<node*,node*>pii; 42 inline int get_size(node *x){ 43 if(x==NULL) 44 return 0; 45 return x->size; 46 } 47 inline L get_hash(node *x){ 48 if(x==NULL) 49 return 0; 50 return x->has; 51 } 52 inline void node::pushup(){ 53 size=1; 54 size+=get_size(ch[0])+get_size(ch[1]); 55 has=get_hash(ch[0])*xp[get_size(ch[1])+1]+c*xp[get_size(ch[1])]+get_hash(ch[1]); 56 } 57 char s[100005]; 58 inline node* build(){ 59 node *x,*las; 60 for(int i=1;i<=n;i++){ 61 x=new node(s[i]); 62 las=NULL; 63 while(top&&st[top]->key>x->key){ 64 st[top]->pushup(); 65 las=st[top]; 66 st[top--]=NULL; 67 } 68 if(top) 69 st[top]->ch[1]=x; 70 x->ch[0]=las; 71 st[++top]=x; 72 } 73 while(top) 74 st[top--]->pushup(); 75 return st[1]; 76 } 77 inline node* merge(node *x,node *y){ 78 if(x==NULL) 79 return y; 80 if(y==NULL) 81 return x; 82 if(x->key<y->key){ 83 // x->pushdown(); 84 x->ch[1]=merge(x->ch[1],y); 85 x->pushup(); 86 return x; 87 } 88 else{ 89 // y->pushdown(); 90 y->ch[0]=merge(x,y->ch[0]); 91 y->pushup(); 92 return y; 93 } 94 } 95 inline pii split(node *x,int k){ 96 if(!x) 97 return pii(NULL,NULL); 98 pii y; 99 // x->pushdown(); 100 if(get_size(x->ch[0])>=k){ 101 y=split(x->ch[0],k); 102 x->ch[0]=y.second; 103 x->pushup(); 104 y.second=x; 105 } 106 else{ 107 y=split(x->ch[1],k-get_size(x->ch[0])-1); 108 x->ch[1]=y.first; 109 x->pushup(); 110 y.first=x; 111 } 112 return y; 113 } 114 inline void insert(char x,int k){ 115 pii tp(split(root,k)); 116 node *tmp(new node(x)); 117 root=merge(merge(tp.first,tmp),tp.second); 118 ++n; 119 } 120 inline void change(char x,int k){ 121 pii s1(split(root,k)); 122 pii s2(split(s1.first,k-1)); 123 s2.second->has=s2.second->c=x; 124 s1.first=merge(s2.first,s2.second); 125 root=merge(s1.first,s1.second); 126 } 127 inline L gethash(int x,int y){ 128 if(x>y) 129 return 0; 130 pii s1(split(root,y)); 131 pii s2(split(s1.first,x-1)); 132 L ret(s2.second->has); 133 s1.first=merge(s2.first,s2.second); 134 root=merge(s1.first,s1.second); 135 return ret; 136 } 137 inline int query(int x,int y){ 138 int l(0),r(min(n-x+1,n-y+1)),mid,ans(0); 139 while(l<=r){ 140 mid=(l+r)>>1; 141 if(gethash(x,x+mid-1)==gethash(y,y+mid-1)) 142 ans=mid,l=mid+1; 143 else 144 r=mid-1; 145 } 146 return ans; 147 } 148 char op[2],ch[2]; 149 int main(){ 150 xp[0]=1; 151 for(int i=1;i<=100000;++i) 152 xp[i]=xp[i-1]*p; 153 scanf("%s",s+1); 154 n=strlen(s+1); 155 root=build(); 156 m=read(); 157 for(int i=1;i<=m;i++){ 158 scanf("%s",op); 159 if(op[0]=='Q'){ 160 int x(read()),y(read()); 161 printf("%d\n",query(x,y)); 162 } 163 if(op[0]=='R'){ 164 int x(read()); 165 scanf("%s",ch); 166 change(ch[0],x); 167 } 168 if(op[0]=='I'){ 169 int x(read()); 170 scanf("%s",ch); 171 insert(ch[0],x); 172 } 173 } 174 }
大视野上不要用$srand$,亲测$RE$= =