3682: Phorni 后缀平衡树 线段树
国际惯例的题面:
考虑如果没有强制在线我们能怎么水掉这个题,先构造出字符串,各种方法求一下后缀数组,然后线段树维护区间rank最小的位置即可。
然而他要求强制在线,支持插入后缀,并比较后缀大小(求rank)的数据结构,当然就是后缀平衡树啦。
于是插入字符串的操作,我们只需要在后缀平衡树上插入这个后缀。此时不需要对线段树进行修改,因为线段树中任何一个位置均不包含新插入的这个后缀(保证信息合法)。
什么你说插入会改变每个后缀的rank值?没关系我们不需要知道每个后缀的rank具体是多少,我们只需要它们的相对大小关系,这个显然是不会改变的是吧。
(你还不明白?先去A了"Bzoj3600: 没有人的算术"再说。什么你A了还不明白?丢人!褪裙吧!)
然后对线段树的修改和查询就显然了。
(话说这题treap不旋比旋转快,替罪羊alpha设为1最快,某大佬裸BST直接AC都是什么鬼啊)
代码:
#include<cstdio>
#include<algorithm>
#include<cctype>
const int maxe=1e6+1e2;
const double alpha = 0.85; char in[maxe];
int at[maxe]; // suffix i's node .
double v[maxe]; struct SuffixBalancedTree { // we should insert 0 as the minimal suffix .
int lson[maxe],rson[maxe],siz[maxe],sf[maxe],root,cnt;
int seq[maxe],sql;
int fail,failfa;
double vfl,vfr; inline bool cmp(int x,int y) {
if( !x || !y ) return !x;
if( in[x] != in[y] ) return in[x] < in[y];
else return v[at[x-]] < v[at[y-]];
}
inline void upgrade(int pos,double l,double r) {
siz[pos] = siz[lson[pos]] + siz[rson[pos]] + ;
if( std::max( siz[lson[pos]] , siz[rson[pos]] ) > siz[pos] * alpha ) fail = pos , failfa = - , vfl = l , vfr = r;
else if( fail == lson[pos] || fail == rson[pos] ) failfa = pos;
}
inline void insert(int &pos,double l,double r,const int &id) {
if( !pos ) {
v[at[id]=pos=++cnt]= ( l + r ) / 2.0 , siz[pos] = , sf[pos] = id;
return;
} const double vmid = ( l + r ) / 2.0;
if( cmp(sf[pos],id) ) insert(rson[pos],vmid,r,id) , upgrade(pos,l,r); // id > sf[pos] .
else insert(lson[pos],l,vmid,id) , upgrade(pos,l,r);
}
inline int rebuild(int ll,int rr,double l,double r) {
const int mid = ( ll + rr ) >> , pos = seq[mid];
const double vmid = ( l + r ) / 2.0; v[pos] = vmid , siz[pos] = rr - ll + ;
if( ll < mid ) lson[pos] = rebuild(ll,mid-,l,vmid);
if( mid < rr ) rson[pos] = rebuild(mid+,rr,vmid,r);
return pos;
}
inline void dfs(int pos) {
if(lson[pos]) dfs(lson[pos]);
seq[++sql] = pos;
if(rson[pos]) dfs(rson[pos]);
lson[pos] = rson[pos] = siz[pos] = ;
}
inline void insert(const int &id) {
fail = , failfa = - , insert(root,,,id);
if(fail) {
sql = , dfs(fail);
if( ~failfa ) {
if( fail == lson[failfa] ) lson[failfa] = rebuild(,sql,vfl,vfr);
else rson[failfa] = rebuild(,sql,vfl,vfr);
} else root = rebuild(,sql,,);
}
}
}sbt; int cov[maxe>>]; struct SegmentTree {
int mx[maxe<<];
#define lson(pos) (pos<<1)
#define rson(pos) (pos<<1|1)
inline bool cmp(int a,int b) {
if( cov[a] == cov[b] ) return a < b;
return v[at[cov[a]]] < v[at[cov[b]]];
}
inline void upgrade(int pos) {
mx[pos] = cmp(mx[lson(pos)],mx[rson(pos)]) ? mx[lson(pos)] : mx[rson(pos)];
}
inline void build(int pos,int l,int r) {
if( l == r ) return void( mx[pos] = l );
const int mid = ( l + r ) >> ;
build(lson(pos),l,mid) , build(rson(pos),mid+,r) , upgrade(pos);
}
inline void update(int pos,int l,int r,const int &tar) {
if( l == r ) return; // nothing to update .
const int mid = ( l + r ) >> ;
if( tar <= mid ) update(lson(pos),l,mid,tar);
else update(rson(pos),mid+,r,tar);
upgrade(pos);
}
inline int query(int pos,int l,int r,const int &ll,const int &rr) {
if( ll <= l && r <= rr ) return mx[pos];
const int mid = ( l + r ) >> ;
if( rr <= mid ) return query(lson(pos),l,mid,ll,rr);
else if( ll > mid ) return query(rson(pos),mid+,r,ll,rr);
const int ql = query(lson(pos),l,mid,ll,rr) , qr = query(rson(pos),mid+,r,ll,rr);
return cmp(ql,qr) ? ql : qr;
}
}sgt; inline char nextchar() {
static const int BS = << ;
static char buf[BS],*st=buf+BS,*ed=st;
if( st == ed ) ed = buf + fread(st=buf,,BS,stdin);
return st == ed ? - : *st++;
}
inline void getstr(char* s) {
char c;
while( !isalpha(c=nextchar()) );
do *s++=c; while( isalpha(c=nextchar()) );
}
inline char realchar() {
char c;
while( !isalpha(c=nextchar()) );
return c;
}
inline int getint() {
int ret = , ch;
while( !isdigit(ch=nextchar()) );
do ret=ret*+ch-''; while( isdigit(ch=nextchar()) );
return ret;
} int main() {
static int n,m,len,tpe,lastans;
n = getint() , m = getint() , len = getint() , tpe = getint() , getstr(in+) , std::reverse(in+,in++len);
for(int i=;i<=len;i++) in[i] -= 'a' , sbt.insert(i);
for(int i=;i<=n;i++) cov[i] = getint();
sgt.build(,,n);
for(int i=,o,c,x,l,r;i<=m;i++) {
o = realchar();
if( o == 'I' ) {
c = getint();
if( tpe ) c ^= lastans;
in[++len] = c , sbt.insert(len);
} else if( o == 'C' ) x = getint() , cov[x] = getint() , sgt.update(,,n,x);
else if( o == 'Q' ) l = getint() , r = getint() , printf("%d\n",lastans=sgt.query(,,n,l,r));
}
return ;
}
思い出して 優しいハウリング
回忆起 温柔的振鸣
奏でる声 未来を示してた
演奏之声 昭示了未来
思い出して 優しいハウリング
回忆起 温柔的共鸣
遅くはない そう教えてくれた
永远不会太迟 你是这样教我的
今までの過ちすべて
至今经历的一切
巻き戻すことなんて出来はしない
已不能倒带
間違いを認める勇気
不懂得承认错误的勇气
知らなかったよ 凄く不器用に 生きてた
十分笨拙的生存着
3682: Phorni 后缀平衡树 线段树的更多相关文章
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)
题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...
- BZOJ1396: 识别子串(后缀自动机 线段树)
题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...
- 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)
题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...
随机推荐
- 如何用MoveIt快速搭建机器人运动规划平台?
MoveIt = RobotGo,翻译成中文就是“机器人,走你!”所以,MoveIt的主要就是一款致力于让机器人能够自主运动及其相关技术的软件,它的所有模块都是围绕着运动规划的实现而设计的. 两个月前 ...
- AutoMapper中用户自定义转换
Custom Type Converters Sometimes, you need to take complete control over the conversion of one type ...
- ubuntu 删除自带软件的方法
$ sudo dpkg -l | grep -i "need2del" $ sudo dpkg -P 或者: $ sudo apt-get --purge remove need2 ...
- xpath与nodejs解析xml
测试xpath的工具 http://www.freeformatter.com/xpath-tester.html#ad-output http://www.xpathtester.com/test ...
- centos 命令和
一.远程工具 Window系统上 Linux 远程登录客户端有SecureCRT, Putty, SSH Secure Shell.TightVNC... 重点推荐一款 FinallShell,一般人 ...
- plsql developer如何自定义快捷键
首选项 用户界面 编辑器 自动替换 选择替换文件,文件内容: sf=select * from df=delete from
- js几种创建对象的方式
javascript是一种“基于prototype的面向对象语言“,与java有非常大的区别,无法通过类来创建对象.那么,既然是面象对象的,如何来创建对象呢? 一.通过”字面量“方式创建. 方法:将成 ...
- OCM_第十九天课程:Section9 —》Data Guard _ DATA GUARD 原理/DATA GUARD 应用/DATA GUARD 搭建
注:本文为原著(其内容来自 腾科教育培训课堂).阅读本文注意事项如下: 1:所有文章的转载请标注本文出处. 2:本文非本人不得用于商业用途.违者将承当相应法律责任. 3:该系列文章目录列表: 一:&l ...
- vue+element之多表单验证
方法一:利用promise var p1=new Promise(function(resolve, reject) { this.$refs[form1].validate((valid) => ...
- 树链剖分边权模板spoj375
树链剖分是树分解成多条链来解决树上两点之间的路径上的问题 如何求出树链:第一次dfs求出树上每个结点的大小和深度和最大的儿子,第二次dfs就能将最大的儿子串起来并hash(映射)到线段树上(或者其他数 ...