bzoj 1014[JSOI2008]火星人prefix - 二分 + hash + splay

1014: [JSOI2008]火星人prefix

Time Limit: 10 Sec  Memory Limit: 162 MB

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个数据,没有插入操作。

 

 

我们发现要支持修改操作,所以后缀数组就不适用了

查询两个字符串的lcp有两个很常见的算法, 后缀数组和 二分哈希

所以对于字符串的修改我们用一个splay 来维护, 平衡树每个节点表示的是对应子树的字符串的哈希值。

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define LL long long
  6 using namespace std;
  7 
  8 const int MAXN = 1e5 + 10, MAXM = 2e5 + 10;
  9 int N, M;
 10 int root;
 11 int ans = 0;
 12 int tot = 0;
 13 char opr[30], d[30];
 14 unsigned long long pow[MAXN];
 15 struct spl {
 16     int ch[2];
 17     int fa;
 18     unsigned LL c;
 19     int size;
 20     unsigned LL hash;
 21     void init()
 22     {
 23         ch[0] = ch[1] = 0;
 24         size = 0;
 25         hash = 0;
 26         c = 0;
 27         fa = 0;
 28     }
 29 } t[MAXN * 50];
 30 inline LL read()
 31 {
 32     LL x = 0, w = 1; char ch = 0;
 33     while(ch < '0' || ch > '9') {
 34         if(ch == '-') {
 35             w = -1;
 36         }
 37         ch = getchar();
 38     }    
 39     while(ch >= '0' && ch <= '9') {
 40         x = x * 10 + ch - '0';
 41         ch = getchar();
 42     }
 43     return x * w;
 44 }
 45 
 46 char s[MAXN];
 47 
 48 void pushup(int k)
 49 {
 50     t[k].size = t[t[k].ch[0]].size + t[t[k].ch[1]].size + 1;
 51     t[k].hash = t[t[k].ch[0]].hash * pow[t[t[k].ch[1]].size + 1] + t[k].c * pow[t[t[k].ch[1]].size] + t[t[k].ch[1]].hash;
 52 }
 53 
 54 void rotate(int x, int &k)
 55 {
 56     int y = t[x].fa, z = t[y].fa;
 57     int l = 0, r;
 58     if(y == k) {
 59         k = x;
 60     }
 61     if(t[y].ch[0] == x) {
 62         l = 0;
 63     } else {
 64         l = 1;
 65     }
 66     r = l ^ 1;
 67     if(z) {
 68         if(t[z].ch[0] == y) {
 69             t[z].ch[0] = x;
 70         } else {
 71             t[z].ch[1] = x;
 72         }
 73     }
 74     int tt = t[x].ch[r];
 75     t[x].ch[r] = y, t[x].fa = z, t[tt].fa = y;
 76     t[y].ch[l] = tt, t[y].fa = x;
 77     pushup(y), pushup(x);
 78 }
 79 
 80 void splay(int x, int &k)
 81 {
 82     while(x != k) {
 83         int y = t[x].fa, z = t[y].fa;
 84         if(y != k) {
 85             if(t[z].ch[0] == y ^ t[y].ch[0] == x) {
 86                 rotate(x, k);
 87             } else {
 88                 rotate(y, k);
 89             }
 90         }
 91         rotate(x, k);
 92     }
 93 }
 94 
 95 unsigned LL judge(int st, int len)
 96 {
 97     int k = root;
 98     int loc = st;
 99     while(loc != t[t[k].ch[0]].size + 1) {
100         if(loc > t[t[k].ch[0]].size) {
101             loc = loc - t[t[k].ch[0]].size - 1;
102             k = t[k].ch[1];
103         } else {
104             k = t[k].ch[0];    
105         }
106     }
107     splay(k, root);
108     k = root;
109     loc = st + len + 1;
110     while(loc != t[t[k].ch[0]].size + 1) {
111         if(loc > t[t[k].ch[0]].size) {
112             loc = loc - t[t[k].ch[0]].size - 1;
113             k = t[k].ch[1];
114         } else {
115             k = t[k].ch[0];    
116         }
117     }
118     splay(k, t[root].ch[1]);
119     return t[t[t[root].ch[1]].ch[0]].hash;
120 }
121 
122 void query(int x, int y)
123 {
124     int l = 0, r = tot - max(x, y) - 1;
125     while(l <= r) {
126         int mid = (l + r) >> 1;
127     //    cout<<mid<<endl;
128         if(judge(x, mid) == judge(y, mid)) {
129             ans = mid;
130             l = mid + 1;
131         } else {
132             r = mid - 1;
133         }
134     }
135     printf("%d\n", ans);
136 }
137 
138 void update(int loc, char d)
139 {
140     int k = root;
141     while(loc != t[t[k].ch[0]].size + 1) {
142         if(loc > t[t[k].ch[0]].size) {
143             loc = loc - t[t[k].ch[0]].size - 1;
144             k = t[k].ch[1];
145         } else {
146             k = t[k].ch[0];    
147         }
148     }
149     t[k].c = d - 'a';
150 //    pushup(k);
151     splay(k, root);
152 }
153 
154 void insert(int x, char d)
155 {
156     int k = root;
157     int loc = x;
158     while(loc != t[t[k].ch[0]].size + 1) {
159         if(loc > t[t[k].ch[0]].size) {
160             loc = loc - t[t[k].ch[0]].size - 1;
161             k = t[k].ch[1];
162         } else {
163             k = t[k].ch[0];    
164         }
165     }
166     splay(k, root);
167     loc = x + 1;
168     k = root;
169     while(loc != t[t[k].ch[0]].size + 1) {
170         if(loc > t[t[k].ch[0]].size) {
171             loc = loc - t[t[k].ch[0]].size - 1;
172             k = t[k].ch[1];
173         } else {
174             k = t[k].ch[0];    
175         }
176     }
177     splay(k, t[root].ch[1]);
178     t[++tot].init();
179     t[t[root].ch[1]].ch[0] = tot;
180     t[tot].fa = t[root].ch[1];
181     t[tot].size = 1;
182     t[tot].hash = t[tot].c = d - 'a';
183     pushup(t[root].ch[1]);
184     pushup(root);
185 }
186 
187 void build(int l, int r, int &k, int last)
188 {
189 //    cout<<l<<" "<<r<<endl;
190     if(l > r) {
191         return;
192     }
193     k = ++tot;
194     if(l == r) {
195         t[k].init();
196         t[k].size = 1;
197         t[k].c = t[k].hash = s[l] - 'a';
198         t[k].fa = last;
199         return;
200     }
201     int mid = (l + r) >> 1;
202     t[k].init();
203     t[k].size = 1;
204     t[k].c = s[mid] - 'a';
205     t[k].fa = last;
206     build(l, mid - 1, t[k].ch[0], k);
207     build(mid + 1, r, t[k].ch[1], k);
208     pushup(k); 
209 }
210 
211 void init()
212 {
213     pow[0] = 1;
214     for(int i = 1; i <= 1e5; i++) {
215         pow[i] = pow[i - 1] * 27;
216     }
217 }
218 
219 void print(int k)
220 {
221 //    cout<<t[k].ch[0]<<" "<<t[k].ch[1]<<" "<<char(t[k].c  + 'a')<<" "<<t[k].size<<" "<<t[k].hash<<endl;
222 
223     if(t[k].ch[0]) {
224         print(t[k].ch[0]);
225     }
226     //cout<<char(t[k].c + 'a');
227     if(t[k].ch[1]) {
228         print(t[k].ch[1]);
229     }
230 }
231 
232 int main()
233 {
234     init();
235 /*    for(int i = 1; i <= 10; i++) {
236         cout<<pow[i]<<" ";
237     }
238     cout<<endl;*/
239     t[0].init();
240     scanf("%s", s + 1);
241     N = strlen(s + 1);
242     N++;
243     s[0] = 'A';
244     s[N] = 'A';
245     build(0, N, root, 0);
246 //    print(root);
247     M = read();
248     while(M--) {
249         scanf("%s", opr);
250         if(opr[0] == 'Q') {
251             int x = read(), y = read();
252             query(x, y);
253         } else if(opr[0] == 'R') {
254             int    x = read();
255             scanf("%s", d);
256             update(x + 1, d[0]);
257         } else {
258             int x = read();
259             scanf("%s", d);
260             insert(x + 1, d[0]);
261         }
262     }
263     return 0;
264 }
265 
266 /*
267 madamimadam
268 7
269 Q 1 7
270 Q 4 8
271 Q 10 11
272 R 3 a
273 Q 1 7
274 I 10 a
275 Q 2 11
276 */
View Code

 

转载于:https://www.cnblogs.com/wuenze/p/8438103.html

【问题描述】 人类终于登上了火星的土地并且见到了神秘的火星人。人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回答。 火星人用一种非常简单的方式来表示数字——掰手指。火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为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、付费专栏及课程。

余额充值