bzoj1014 [JSOI2008]火星人prefix
1014: [JSOI2008]火星人prefix
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4028 Solved: 1243
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、 询问。语法:Q x y,x, y均为正整数。功能:计算LCQ(x, y) 限制:1 <= x, y <= 当前字符串长度。 2、 修改。语法:R x d,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字符串长度。 3、 插入:语法:I x d,x是非负整数,d是字符。功能:在字符串第x个字符之后插入字符d,如果x = 0,则在字符串开头插入。限制:x不超过当前字符串长度。
Output
对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。
Sample Input
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11
Sample Output
1
0
2
1
HINT
数据规模:
对于100%的数据,满足:
1、 所有字符串自始至终都只有小写字母构成。
2、 M <= 150,000
3、 字符串长度L自始至终都满足L <= 100,000
4、 询问操作的个数不超过10,000个。
对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。
Source
题意:要求维护一个字符串,并要求三个操作,
1、快速回答 第x位开始的后缀 和 第y位开始的后缀 的共同前缀的长度
2、修改第 x 位字符为另一个字符
3、在第x位字符后面添加一个字符
分析:本题较常规,也涉及的知识也比较简单
根据题目的范围以及动态修改,显然像后缀数组等等高大上的无法修改的数据结构可以不用考虑
再细看题目,查询不超过1000,说明查询的复杂度可以稍微高一点
灵光一闪,不就是用splay去维护这个字符串就可以了吗?
三个操作都可以实现,
Splay每个节点维护一个Hash值,表示这个节点和它孩子们包括的那段字符串的Hash值,这样就可以Log(Len)内知道某一段的Hash值了
1、查询,二分最长后缀的长度 L,并用Splay求取a....a+Mid-1和b......b+Mid-1这两段的Hash值比较是否相等
2、3、两操作都是Splay的常见操作,不应该不会,Hash值重新算就好
综上所述,本题得解
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <deque> 6 #include <vector> 7 #include <queue> 8 #include <iostream> 9 #include <algorithm> 10 #include <map> 11 #include <set> 12 #include <ctime> 13 using namespace std; 14 typedef long long LL; 15 typedef double DB; 16 #define For(i, s, t) for(int i = (s); i <= (t); i++) 17 #define Ford(i, s, t) for(int i = (s); i >= (t); i--) 18 #define MIT (2147483647) 19 #define INF (1000000001) 20 #define MLL (1000000000000000001LL) 21 #define sz(x) ((int) (x).size()) 22 #define clr(x, y) memset(x, y, sizeof(x)) 23 #define puf push_front 24 #define pub push_back 25 #define pof pop_front 26 #define pob pop_back 27 #define ft first 28 #define sd second 29 #define mk make_pair 30 inline void SetIO(string Name) { 31 string Input = Name+".in", 32 Output = Name+".out"; 33 freopen(Input.c_str(), "r", stdin), 34 freopen(Output.c_str(), "w", stdout); 35 } 36 37 const int N = 100010, Mod = 9875321; 38 struct Node { 39 int Fa, Child[2]; 40 int Dat, Sum, Hash; 41 42 Node() { 43 Fa = 0, clr(Child, 0), Dat = Sum = 0; 44 } 45 46 #define Fa(x) (Tr[x].Fa) 47 #define C(x, y) (Tr[x].Child[y]) 48 #define Lch(x) (Tr[x].Child[0]) 49 #define Rch(x) (Tr[x].Child[1]) 50 #define Dat(x) (Tr[x].Dat) 51 #define Sum(x) (Tr[x].Sum) 52 #define Hash(x) (Tr[x].Hash) 53 } Tr[N]; 54 int Tot, Root; 55 int m, Len; 56 int Fact[N]; 57 string S; 58 59 inline void Input() { 60 cin>>S; 61 scanf("%d", &m); 62 } 63 64 inline void Updata(int x) { 65 Sum(x) = 1; 66 For(i, 0, 1) Sum(x) += Sum(C(x, i)); 67 LL Cnt = Hash(Lch(x))*27+Dat(x); 68 Cnt = Cnt*Fact[Sum(Rch(x))]+Hash(Rch(x)); 69 Hash(x) = Cnt%Mod; 70 } 71 72 inline void Rotate(int x, int T) { 73 int y, z; 74 y = Fa(x); z = Fa(y); 75 if(Lch(z) == y) Lch(z) = x; 76 else if(Rch(z) == y) Rch(z) = x; 77 Fa(x) = z; 78 C(y, !T) = C(x, T), Fa(C(x, T)) = y; 79 C(x, T) = y, Fa(y) = x; 80 Updata(y); 81 } 82 83 inline void Splay(int x, int Goal) { 84 int y, z, T1, T2; 85 while(Fa(x) != Goal) { 86 y = Fa(x); z = Fa(y); 87 T1 = (Lch(y) == x), T2 = (Lch(z) == y); 88 if(z == Goal) Rotate(x, T1); 89 else if(T1^T2) { 90 Rotate(x, T1); 91 Rotate(x, T2); 92 } else { 93 Rotate(y, T2); 94 Rotate(x, T1); 95 } 96 } 97 Updata(x); 98 if(!Goal) Root = x; 99 } 100 101 inline int Find(int w) { 102 int x = Root; 103 while(w) { 104 if(w <= Sum(Lch(x))) x = Lch(x); 105 else if(w == Sum(Lch(x))+1) break; 106 else { 107 w -= 1+Sum(Lch(x)); 108 x = Rch(x); 109 } 110 } 111 112 Splay(x, 0); 113 return x; 114 } 115 116 inline void Insert(int w, int x) { 117 int L, R; 118 L = Find(w), R = Find(w+1); 119 Splay(L, 0); Splay(R, L); 120 Lch(R) = ++Tot; 121 Fa(Tot) = R, Dat(Tot) = x; 122 Splay(Tot, 0); 123 } 124 125 inline int Query(int L, int R) { 126 int x, y; 127 x = Find(L-1), y = Find(R+1); 128 Splay(x, 0); Splay(y, x); 129 return Hash(Lch(y)); 130 } 131 132 inline int Work(int A, int B) { 133 if(A > B) swap(A, B); 134 int Left = 0, Right = Len-B+1, Mid, Ret, Hash1, Hash2; 135 A++, B++; 136 while(Left <= Right) { 137 Mid = (Left+Right)>>1; 138 Hash1 = Query(A, A+Mid-1); 139 Hash2 = Query(B, B+Mid-1); 140 if(Hash1 == Hash2) { 141 Ret = Mid; 142 Left = Mid+1; 143 } else Right = Mid-1; 144 } 145 146 return Ret; 147 } 148 149 inline void Change(int w, int x) { 150 int A; 151 A = Find(w); 152 Dat(A) = x; 153 Updata(A); 154 } 155 156 inline void Solve() { 157 Fact[0] = 1; 158 For(i, 1, N-1) Fact[i] = (Fact[i-1]*27)%Mod; 159 160 Tot = 2, Root = 1; 161 Rch(1) = 2, Sum(1) = 2; 162 Fa(2) = 1, Sum(2) = 1; 163 164 Len = S.length(); 165 For(i, 0, Len-1) { 166 int x = S[i]-'a'+1; 167 Insert(i+1, x); 168 } 169 170 while(m--) { 171 char Ch = ' ', x = ' '; 172 while(!(Ch >= 'A' && Ch <= 'Z')) Ch = getchar(); 173 int a, b, Ret; 174 175 if(Ch == 'Q') { 176 scanf("%d%d", &a, &b); 177 Ret = Work(a, b); 178 printf("%d\n", Ret); 179 } else if(Ch == 'I') { 180 scanf("%d", &a); 181 while(!(x >= 'a' && x <= 'z')) x = getchar(); 182 b = x-'a'+1; 183 Insert(a+1, b); 184 Len++; 185 } else { 186 scanf("%d", &a); 187 while(!(x >= 'a' && x <= 'z')) x = getchar(); 188 b = x-'a'+1; 189 Change(a+1, b); 190 } 191 } 192 } 193 194 int main() { 195 SetIO("1014"); 196 Input(); 197 Solve(); 198 return 0; 199 }