2018.06.28 BZOJ1014 [JSOI2008]火星人prefix(非旋treap+hash)
[JSOI2008]火星人prefix
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 8951 Solved: 2860
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个数据,没有插入操作。
题意简述:维护一个字符串中任意两段后缀的最长前缀,支持修改和插入操作。
我们先来思考一下静态版本,如果让我们维护字符串中任意两段后缀的最长前缀,怎么做?后缀数组?
其实我们可以用更简单的二分+哈希的方法来替代,尽管二分+哈希的效率不如前者优秀,但是码量少,思维难度小,并且实际测试并不慢。
动态版本?
但当我们看到插入操作时,不难联想到这是一道平衡树的题目,怎么维护?
如果我们按每个字符在序列中的位置为序建立这棵平衡树来维护区间的哈希值的话,每次只需提取出(ql,n)(ql,n)(ql,n)和(qr,n)(qr,n)(qr,n)来二分答案即可。
代码如下
#include<bits/stdc++.h>
#define M 1000010
#define bas 37
using namespace std;
typedef pair<int,int> res;
unsigned int hash[M],po[M];
int q,son[M][2],siz[M],v[M],rd[M],tot=0,root=0;
inline void pushup(int p){
siz[p]=siz[son[p][0]]+siz[son[p][1]]+1;
hash[p]=hash[son[p][0]]+v[p]*po[siz[son[p][0]]]+hash[son[p][1]]*po[siz[son[p][0]]+1];
}
inline int merge(int a,int b){
if(!a||!b)return a+b;
if(rd[a]<rd[b]){son[a][1]=merge(son[a][1],b),pushup(a);return a;}
son[b][0]=merge(a,son[b][0]),pushup(b);return b;
}
inline res split(int a,int k){
if(!a)return res(0,0);
res ans,tmp;
if(siz[son[a][0]]>=k){
tmp=split(son[a][0],k);
son[a][0]=tmp.second,pushup(a);
ans.first=tmp.first,ans.second=a;
return ans;
}
tmp=split(son[a][1],k-siz[son[a][0]]-1);
son[a][1]=tmp.first,pushup(a);
ans.first=a,ans.second=tmp.second;
return ans;
}
inline int build(int val){
int ret=++tot;
hash[tot]=v[tot]=val;
son[tot][0]=son[tot][1]=0;
rd[tot]=rand();
siz[tot]=1;
return tot;
}
inline void insert(int pos,int v){
res tmp=split(root,pos);
int p=build(v);
root=merge(merge(tmp.first,p),tmp.second);
}
inline void erase(int pos){
res x=split(root,pos);
res y=split(x.first,pos-1);
root=merge(y.first,x.second);
}
char s[150005];
inline int ask(int p,int len){
if(p+len-1>siz[root])return -1;
res x=split(root,p-1);
res y=split(x.second,len);
int ret=hash[y.first];
root=merge(x.first,merge(y.first,y.second));
return ret;
}
inline void query(int ql,int qr){
int l=0,r=siz[root]-qr+1,ans=0;
while(l<=r){
int mid=l+r>>1;
int ax=ask(ql,mid),ay=ask(qr,mid);
if(ax==-1||ay==-1){r=mid-1;continue;}
if(ax==ay)ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
int main(){
po[0]=1;
for(int i=1;i<=100000;++i)po[i]=po[i-1]*bas;
scanf("%s",s+1);
int len=strlen(s+1),l,r;
for(int i=1;i<=len;++i)root=merge(root,build(s[i]-'a'));
scanf("%d",&q);
while(q--){
char op[3],c[3];
scanf("%s",op);
if(op[0]=='Q'){
scanf("%d%d",&l,&r);
query(l,r);
}
if(op[0]=='R'){
scanf("%d%s",&l,c);
erase(l);
insert(l-1,c[0]-'a');
}
if(op[0]=='I'){
scanf("%d%s",&l,&c);
insert(l,c[0]-'a');
}
}
return 0;
}
2018.06.28 BZOJ1014 [JSOI2008]火星人prefix(非旋treap+hash)的更多相关文章
- [Bzoj1014][JSOI2008]火星人prefix(无旋Treap&hash)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1014 因为涉及到增加和修改,所以后缀数组就被pass掉了,想到的就是平衡树维护hash值 ...
- BZOJ1014 JSOI2008 火星人prefix 【非旋转Treap】*
BZOJ1014 JSOI2008 火星人prefix Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符 ...
- [BZOJ1014][JSOI2008]火星人prefix
[BZOJ1014][JSOI2008]火星人prefix 试题描述 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字 ...
- bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix
http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include< ...
- Jewel Magic UVA - 11996 || bzoj1014: [JSOI2008]火星人prefix
Jewel Magic UVA - 11996 这是一道用splay/非旋treap做的题(这里用的是非旋treap) 1/2/3是splay/非旋treap的常规操作.对于操作4,可以用哈希法求LC ...
- 2018.08.06 bzoj1500: [NOI2005]维修数列(非旋treap)
传送门 平衡树好题. 我仍然是用的fhqtreap,感觉速度还行. 维护也比线段树splay什么的写起来简单. %%%非旋treap大法好. 代码: #include<bits/stdc++.h ...
- [bzoj1014][JSOI2008]火星人prefix_非旋转Treap_hash_二分
火星人prefix bzoj-1014 JSOI-2004 题目大意:给定一个字符串,支持三种操作:1.查询:两个后缀之间的$LCP$:2.单点修改:3.插入一个字符. 注释:$1\le n\le 1 ...
- [BZOJ1014] [JSOI2008] 火星人prefix (splay & 二分答案)
Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 ...
- bzoj1014: [JSOI2008]火星人prefix splay+hash+二分
Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 ...
随机推荐
- Java架构技术知识点梳理
Java程序员应该加以重视: 吃透基础技术 养成良好的阅读源码的习惯 有长期的技术学习规划 下面,我们来一起逐条看看,特别是第 3 个方法. 想要做到年薪50万,首先你自己必须是高水平的程序员! 能年 ...
- 表格(table)
Title 主机名 端口 操作 1111 10023 查看详情 修改 表头1 表头1 表头1 表头1 1 1 1 1 1 1 1 1 1 <!DOCTYPE html><html l ...
- Nginx bind() to 0.0.0.0:8000 failed (10013: 错误解决
本人配置Nginx 8000端口, 启动Nginx 失败, 查看日志logs/error.log出现如下提示 结束酷狗进程就Ok叻
- python 3 属性查找与绑定方法
1.属性查找 类有两种属性:数据属性和函数属性 (1)类的数据属性是所有对象共享的 #类的数据属性是所有对象共享的,id都一样 class OldboyStudent: school='oldboy' ...
- eclipse从svn导入maven项目变成普通项目解决办法
右击项目-->configure-->Convert to Maven Project
- 在eclipse中创建maven项目,亲测有效,详细步骤
一.想要使用maven,首先要配置本地maven的环境 1.在http://maven.apache.org/download.cgi中去下载maven 2. 3.下载完毕后将压缩包解压到自己记住的位 ...
- Jenkins服务器磁盘空间爆满问题解决
现象: 从根目录开始,使用du -h -x --max-depth=1 查看哪个目录占用过高,打算对于过高目录中的内容适当删减腾出一些空间 通过追踪查询,发现是jenkins的构建纪录未清理,而且最 ...
- Ibatis/Mybatis模糊查询
Ibatis/Mybatis模糊查询 根据网络内容整理 Ibatis中 使用$代替#.此种方法就是去掉了类型检查,使用字符串连接,不过可能会有sql注入风险. Sql代码 select * from ...
- Haskell语言学习笔记(47)Arrow(2)
Function, Monad, Arrow f :: Int -> (Int, Int) f = \x -> let y = 2 * x z1 = y + 3 z2 = y - 5 in ...
- 【346】TF-IDF
Ref: 文本挖掘预处理之向量化与Hash Trick Ref: 文本挖掘预处理之TF-IDF Ref: sklearn.feature_extraction.text.CountVectorizer ...