[BZOJ1014] [JSOI2008] 火星人prefix (splay & 二分答案)
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
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
1、所有字符串自始至终都只有小写字母构成。
2、M<=150,000
3、字符串长度L自始至终都满足L<=100,000
4、询问操作的个数不超过10,000个。
对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。
Source
Solution
相信有很多人第一眼觉得是后缀数组吧。然而后缀数组无法有效地应付修改操作
这道题比较特殊的一点是询问个数很少,基本集中在插入和修改操作
我们可以用$splay$维护这个字符串,插入和修改按普通$splay$做,the Longest Common Qianzhui$LCQ$需要二分答案:
每个节点维护一个子树$hash$值,二分答案$ans$,不断检查$s[x]\sim s[x +ans]$和$s[y]\sim s[y+ans]$的$hash$值是否一样即可
由于有提取区间的操作,所以$treap$是无法满足的
插入和修改的总复杂度是$O(q_1logn)$,$LCQ$的总复杂度是$O(q_2log^2n)$,因为$q_2$相对较小,所以整体可以看成$O(qlogn)$的
#include <bits/stdc++.h>
using namespace std;
struct spaly
{
int c[], fa, val, siz, hash;
int& operator[] (int x)
{
return c[x];
}
}a[];
char s[], op[];
int pwr[]; void push_up(int k)
{
int x = a[a[k][]].siz;
a[k].siz = a[a[k][]].siz + x + ;
a[k].hash = a[a[k][]].hash * pwr[x + ] + a[k].val * pwr[x] + a[a[k][]].hash;
} void rotate(int &k, int x)
{
int y = a[x].fa, z = a[y].fa, dy = a[y][] == x;
if(k == y) k = x;
else a[z][a[z][] == y] = x;
a[y][dy] = a[x][!dy], a[a[x][!dy]].fa = y;
a[x][!dy] = y, a[y].fa = x, a[x].fa = z;
push_up(y);
} void splay(int &k, int x)
{
while(k != x)
{
int y = a[x].fa, z = a[y].fa;
if(k != y)
if(a[y][] == x ^ a[z][] == y) rotate(k, x);
else rotate(k, y);
rotate(k, x);
}
push_up(x);
} int find_kth(int k, int x)
{
if(x <= a[a[k][]].siz) return find_kth(a[k][], x);
if(x == a[a[k][]].siz + ) return k;
return find_kth(a[k][], x - a[a[k][]].siz - );
} int main()
{
int n, m, x, y, z, root, l, r, mid, ptot;
scanf("%s%d", s, &m);
n = strlen(s);
pwr[] = ;
for(int i = ; i <= ; ++i)
pwr[i] = pwr[i - ] * ;
a[].fa = , a[].siz = ;
for(int i = ; i <= n + ; ++i)
{
a[i][] = i - , a[i].fa = i + ;
a[i].val = s[i - ], push_up(i);
}
a[n + ].fa = , root = ptot = n + ;
while(m--)
{
scanf("%s", op);
if(op[] == 'R')
{
scanf("%d%s", &x, op);
splay(root, find_kth(root, x + ));
a[root].val = op[], push_up(root);
}
else if(op[] == 'I')
{
scanf("%d%s", &x, op), ++n;
splay(root, find_kth(root, x + ));
splay(a[root][], find_kth(root, x + ));
a[a[root][]][] = ++ptot;
a[ptot].val = a[ptot].hash = op[];
a[ptot].fa = a[root][], a[ptot].siz = ;
push_up(a[root][]), push_up(root);
}
else
{
scanf("%d%d", &x, &y);
if(x > y) swap(x, y);
l = , r = n - y + , z = ;
while(l < r - )
{
mid = (l + r) >> ;
splay(root, find_kth(root, x));
splay(a[root][], find_kth(root, x + mid + ));
z = a[a[a[root][]][]].hash;
splay(root, find_kth(root, y));
splay(a[root][], find_kth(root, y + mid + ));
z -= a[a[a[root][]][]].hash;
z ? r = mid : l = mid;
}
printf("%d\n", l);
}
}
return ;
}
[BZOJ1014] [JSOI2008] 火星人prefix (splay & 二分答案)的更多相关文章
- BZOJ 1014 [JSOI2008]火星人prefix (splay+二分答案+字符串hash)
题目大意:维护一个字符串,支持插入字符和替换字符的操作,以及查询该字符串两个后缀的最长公共前缀长度 乍一看以为是后缀数组,然而并没有可持久化后缀数组(雾) 看题解才知道这是一道splay题,首先要对s ...
- BZOJ1014: [JSOI2008]火星人prefix(splay 二分 hash)
题意 题目链接 Sol 一眼splay + 二分hash,不过区间splay怎么写来着呀 试着写了两个小时发现死活不对 看了一下yyb的代码发现自己根本就不会splay.... // luogu-ju ...
- BZOJ 1014: [JSOI2008]火星人prefix [splay 二分+hash] 【未完】
1014: [JSOI2008]火星人prefix Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 6243 Solved: 2007[Submit] ...
- 【BZOJ-1014】火星人prefix Splay + 二分 + Hash
1014: [JSOI2008]火星人prefix Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 5852 Solved: 1871[Submit] ...
- BZOJ 1014: [JSOI2008]火星人prefix Splay+二分
1014: [JSOI2008]火星人prefix 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=1014 Description 火星人 ...
- [bzoj1014](JSOI2008)火星人 prefix (Splay维护哈希)
Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀. 比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 ...
- bzoj1014: [JSOI2008]火星人prefix splay+hash+二分
Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 ...
- BZOJ1014[JSOI2008]火星人prefix(splay维护hash)
Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 ...
- bzoj1014: [JSOI2008]火星人prefix splay+hash
我写的代码好像自古以来就是bzoj不友好型的 本地跑的比std快,但是交上去巧妙被卡 答案...应该是对的,拍了好久了 #include <bits/stdc++.h> #define M ...
随机推荐
- location对象浅探
- Linux下的压力测试工具:ab、http_load、webbench、siege
一.ab 1.1 介绍 ab是apache自带的一款功能强大的测试工具. 安装了apache一般就自带了. 1.2 下载 同apache. 1.3 安装 同apache. 1.4 安装结果 ...
- C#中引用变量是否应该加ref?
看如下代码: void Test(T t); void Test(ref T t); 当T是值类型的时候,很好判断,第一种并不能改变方法外变量的值,需要第二种方法才可以.通过查看IL代码,可以看到 ...
- core java
ConsoleTest,这个程序如果在IDE里运行就会因无法获得控制台而报错 import java.io.Console; public class ConsoleTest { public sta ...
- LeetCode第六天
第六天 30.(219) Contains Duplicate II JAVA class Solution { public boolean containsNearbyDuplicate(int[ ...
- 计蒜客 无脑博士 bfs
题目链接无脑博士的试管们 思路:直接模拟倒水过程即可,但是需要记忆判断当前的情况是否已经处理过.dfs和bfs都ok AC代码 #include <cstdio> #include < ...
- 理解 Git
Git 如何保存文件 其它版本管理系统通常会保存所有文件及其历次提交的差异(diff / revision),通过 merge 原始文件与各阶段的差异就能获取任何版本的状态 而 Git 保存的是每一次 ...
- SDP(9):MongoDB-Scala - data access and modeling
MongoDB是一种文件型数据库,对数据格式没有硬性要求,所以可以实现灵活多变的数据存储和读取.MongoDB又是一种分布式数据库,与传统关系数据库不同的是,分布式数据库不支持table-join,所 ...
- Ubuntu版本更替所引发的“血案”
Ubuntu版本更替所引发的"血案" 今天,我兴致很高的装了双系统,并且配置了Linux的网络,接下来就是安装一些软件,来实现我想达到的功能了,结果被一个小小的命令卡的 ...
- 【前端】Vue和Vux开发WebApp日志二、优化gulp任务
转载请注明出处:http://www.cnblogs.com/shamoyuu/p/vue_vux_2.html 项目github地址:https://github.com/shamoyuu/vue- ...