题目

这种含有修改操作的就难以用后缀数组实现了,求LCP这种区间相等的类型可以想到用hash判断,同时LCP的答案大小符合二分条件可以二分求出,如果只有修改可以用线段树维护,因为还有有插入操作所以想到平衡树。

#include<cstdio>
const int N = 2e5 + 5;
const int base = 237;
typedef unsigned long long ull;
ull pow[N]; struct Splay{
#define ls p[u].son[0]
#define rs p[u].son[1]
#define maxn N int root = 1, cnt = 2;
struct Node{
int fa, size, len; // size是子树大小,len是除去虚点的子树大小
ull hash, val;
int son[2];
}p[maxn]; inline int identify(int u){
return p[p[u].fa].son[1] == u;
} inline void update(int u){
p[u].size = p[ls].size + p[rs].size + 1;
p[u].len = p[ls].len + p[rs].len + (u > 2); // 判断u > 2是为了除去虚点的影响
p[u].hash = (p[ls].hash * base + p[u].val) * pow[p[rs].len] + p[rs].hash;
} void rotate(int u){
int f = p[u].fa, gf = p[f].fa, sta = identify(u), sta_f = identify(f);
p[f].son[sta] = p[u].son[sta ^ 1];
p[p[f].son[sta]].fa = f;
p[u].son[sta^1] = f, p[f].fa = u, p[u].fa = gf;
p[gf].son[sta_f] = u;
update(f);
} void splay(int u, int goal){
for(int f; (f = p[u].fa) && (f != goal); rotate(u)){
if(p[f].fa != goal) rotate(identify(u) == identify(f) ? f : u);
}
if(!goal) root = u;
update(u);
} ull Hash(int u, int len){
int L = find_Kth(u), R = find_Kth(u + len + 1);
splay(L, 0), splay(R, L);
return p[p[R].son[0]].hash;
} int LCQ(int x, int y){
int L = 0, R = cnt - 1 - y, ans;
while(L <= R){
int mid = (L + R) >> 1;
if(Hash(x, mid) == Hash(y, mid)) L = mid + 1, ans = mid;
else R = mid - 1;
}
return ans;
} int find_Kth(int k){
int u = root;
while(1){
if(p[ls].size + 1 == k) return u;
if(p[ls].size >= k) u = ls;
else k -= p[ls].size + 1, u = rs;
}
} void insert(int u, int val){
int L = find_Kth(u), R = find_Kth(u + 1);
splay(L, 0), splay(R, L);
p[++cnt].val = val, p[R].son[0] = cnt, p[cnt].fa = R;
splay(cnt, 0);
} void modify(int x, int val){
int L= find_Kth(x), R = find_Kth(x + 2);
splay(L, 0), splay(R, L);
p[p[R].son[0]].val = val;
update(p[R].son[0]), update(R), update(L);
}
}Tree; int m, x, y;
char s[N], opt[2]; int main(){
scanf("%s%d", s + 1, &m);
pow[0] = 1;
for(int i = 1; i < N; ++i) pow[i] = pow[i - 1] * base;
// 插入两个虚点作为序列的左右端点,方便插入、查找等操作
Tree.p[1].son[1] = 2, Tree.p[2].fa = 1;
Tree.update(1), Tree.update(2);
for(int i = 1; s[i]; ++i) Tree.insert(i, s[i]);
while(m--){
scanf("%s", opt);
if(opt[0] == 'Q'){
scanf("%d%d", &x, &y);
if(x > y) x ^= y ^= x ^= y;
printf("%d\n", Tree.LCQ(x, y));
}
else if(opt[0] == 'R'){
scanf("%d%s", &x, opt);
Tree.modify(x, opt[0]);
}
else{
scanf("%d%s", &x, opt);
Tree.insert(x + 1, opt[0]);
}
}
return 0;
}

【JSOI2008】火星人 (哈希+Splay)的更多相关文章

  1. bzoj 1014: [JSOI2008]火星人prefix hash && splay

    1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3154  Solved: 948[Submit][ ...

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

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1014 [题意] 给定一个字符串,要求提供修改一个字符,插入一个字符,查询两个后缀LCP ...

  3. bzoj1014: [JSOI2008]火星人prefix(splay+hash+二分)

    题目大意:一个字符串三个操作:①求两个后缀的LCP②插入一个字符③修改一个字符. 前几天刚学了hash+二分求lcp,就看到这题. 原来splay还能这么用?!原来splay模板这么好写?我以前写的s ...

  4. 【BZOJ】1014: [JSOI2008]火星人prefix(splay+hash+二分+lcp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1014 题意:支持插入一个字符.修改一个字符,查询lcp.(总长度<=100000, 操作< ...

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

    题目链接:BZOJ - 1014 题目分析 求两个串的 LCP ,一种常见的方法就是 二分+Hash,对于一个二分的长度 l,如果两个串的长度为 l 的前缀的Hash相等,就认为他们相等. 这里有修改 ...

  6. _bzoj1014 [JSOI2008]火星人prefix【Splay】

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1014 天,写kth()时,把判断条件k <= siz[ch[x][0]]错写成了k & ...

  7. BZOJ 1014: [JSOI2008]火星人prefix [splay 二分+hash] 【未完】

    1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 6243  Solved: 2007[Submit] ...

  8. BZOJ 1014: [JSOI2008]火星人prefix Splay+二分

    1014: [JSOI2008]火星人prefix 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=1014 Description 火星人 ...

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

    用splay维护序列, 二分+hash来判断LCQ.. #include<bits/stdc++.h> using namespace std; typedef unsigned long ...

  10. BZOJ 1014 [JSOI2008]火星人prefix (Splay + Hash + 二分)

    1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 8112  Solved: 2569[Submit] ...

随机推荐

  1. 【技术积累】Linux中的命令行【理论篇】【五】

    arpd命令 命令介绍 arpd命令是Linux系统中的一个网络工具,用于管理和操作ARP(地址解析协议)缓存.ARP协议用于将IP地址映射到MAC地址,以便在局域网中进行通信. 命令说明 arpd命 ...

  2. 如何用windows任务视图管理多个程序,提高.net开发效率

    在 Windows 操作系统中,任务栏是一个非常重要的工具栏,用来显示当前正在运行的程序和任务.如果同时运行了很多程序,任务栏上的图标就会变得非常拥挤,不方便管理和切换.为了提高工作效率,可以通过任务 ...

  3. 使用lame以多进程方式转码wav为mp3

    前言 lame以单进程的方式转码wav文件,之前量少,足够使用.如今每日wav文件数量极多,单进程的效率就不够用了,所以这里使用脚本的方式,启动多个lame进程转码wav文件. code01: aut ...

  4. [gin]数据解析和绑定

    前言 go version: 1.18 本文主要包含JSON.Form.Uri.XML的数据解析与绑定. JSON数据解析与绑定 go代码 package main import ( "ne ...

  5. 基于opencv-pyhton与opencv-c++的结合理解与学习

    2023年上半年,一直在学习opencv-c++版本,学习了其中的多个库函数 笔记链接:https://www.cnblogs.com/Tan-code/category/2339311.html o ...

  6. 如何调用API接口获取商品数据

    在当今数字化的时代,电子商务的崛起使得网购成为了人们生活中不可或缺的一部分.作为电子商务中最为熟知和流行的平台之一,拥有大量的商品资源和用户群体.如果你是一名开发者或者是对数据分析感兴趣的人,你可能会 ...

  7. API对接需求如何做需求调研,需要注意什么?

    随着互联网的发展,越来越多的企业开始将自己的业务系统通过API接口与其他系统进行对接,以便于数据的共享.协同操作等.在进行API对接之前,需要对用户需求进行深入的调研,以便于能够准确的设计出满足用户需 ...

  8. 我找回了我喜欢的Github Old Feed

    前言 这周Github更新了个人主页Feed(指的是用户的活动源或动态源),作为GitHub重度爱好者而言New Feed完全不是我之前所喜欢的效果.虽然说New Feed添加了允许用户可以自定义配置 ...

  9. python入门基础(14)--类的属性、成员方法、静态方法以及继承、重载

    上一篇提到过类的属性,但没有详细介绍,本篇详细介绍一下类的属性 一 .类的属性 方法是用来操作数据的,而属性则是建模必不的内容,而且操作的数据,大多数是属性,比如游戏中的某个boss类,它的生命值就是 ...

  10. containerd镜像拉取配置

    背景: 公司要求部署最一套新版的k8s系统来部署生产应用,说实话很头疼.因为k8s自1.23版本之后就用不docker作为容器的默认运行时了,而是采用的containerd,这就带来了一系列的问题.没 ...