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

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

查询两个字符串的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 */

原文地址:https://www.cnblogs.com/wuenze/p/8438103.html

时间: 2024-10-29 04:47:32

bzoj 1014[JSOI2008]火星人prefix - 二分 + hash + splay的相关文章

BZOJ 1014: [JSOI2008]火星人prefix( splay + hash )

用splay维护序列, 二分+hash来判断LCQ.. #include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int maxn = 100009; const int P = 1000173169; ull K[maxn]; int N; char S[maxn]; struct Node { Node *ch[2], *p; int s, v; ull h; inline void

bzoj 1014: [JSOI2008]火星人prefix hash &amp;&amp; splay

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3154  Solved: 948[Submit][Status] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人

求帮看!!!!BZOJ 1014 [JSOI2008]火星人prefix

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4164  Solved: 1277[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

[BZOJ 1014] [JSOI2008] 火星人prefix 【Splay + Hash】

题目链接:BZOJ - 1014 题目分析 求两个串的 LCP ,一种常见的方法就是 二分+Hash,对于一个二分的长度 l,如果两个串的长度为 l 的前缀的Hash相等,就认为他们相等. 这里有修改字符和插入字符的操作,所以用 Splay 来维护串的 Hash 值. 一个节点的值就是它的子树表示的字串的 Hash 值. 使用 unsigned long long 然后自然溢出就不需要 mod 了,速度会快很多. 代码 #include <iostream> #include <cstd

[BZOJ 1014][JSOI2008]火星人prefix(Splay+二分+hash)

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(

BZOJ 1014 JSOI2008 火星人prefix Splay+Hash+二分

题目大意:给定一个字符串,提供下列操作: 1.查询从x开始的后缀和从y开始的后缀的最长公共前缀长度 2.将x位置的字符修改为y 3.在x位置的字符后面插入字符y 看到这题一开始我先懵住了...这啥..我第一时间想到的是后缀数据结构 但是不会写 而且后缀数据结构也不支持修改操作 后来无奈找了题解才知道是Hash+二分... 太强大了 Hash+二分打爆一切啊 用Splay维护这个字符串的修改和插入操作 每个节点维护子串的Hash值 判断时二分找到最长公共前缀 不过这道题还有一些注意事项 1.此题不

bzoj 1014 [JSOI2008]火星人prefix (splay+二分答案+字符串hash)

题目大意:维护一个字符串,支持插入字符和替换字符的操作,以及查询该字符串两个后缀的最长公共前缀长度 乍一看以为是后缀数组,然而并没有可持久化后缀数组(雾) 看题解才知道这是一道splay题,首先要对splay维护区间信息有一定了解 splay维护,插入字符,替换字符 而它的字树内所有儿子的中序遍历的hash值也可以通过splay维护  (这个推导式似乎烂大街了) 而后缀就是把i-1拎到根节点,然后把n+1拎到根节点的右儿子上,它的左儿子表示的就是hash值 至于如何查公共前缀呢?二分答案啊!询问

bzoj 1014 [JSOI2008]火星人prefix(splay+hash)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1014 [题意] 给定一个字符串,要求提供修改一个字符,插入一个字符,查询两个后缀LCP的功能. [思路]  splay维护字符串的哈希值.因为要提供区间,splay采用先查找后调整至根的写法. 一个结点的hash值为: ch[0]->h * X^(ch[1]->s+1)+v * X^(ch[1]->s)+ch[1]->h     对于一个询问每次二分长度,提取区间后比较

BZOJ 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个字符开始的字串 ,两个字串的公共前缀的长