国际惯例的题面:

考虑如果没有强制在线我们能怎么水掉这个题,先构造出字符串,各种方法求一下后缀数组,然后线段树维护区间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 后缀平衡树 线段树的更多相关文章

  1. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

  2. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  3. 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)

    题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...

  4. BZOJ1396: 识别子串(后缀自动机 线段树)

    题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...

  5. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  6. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  7. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

  8. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

  9. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

随机推荐

  1. Python 3.x 格式化输出字符串 % & format 笔记

    Python 3.x 格式化输出字符串 % & format 笔记 python格式化字符串有%和{}两种 字符串格式控制符. 字符串输入数据格式类型(%格式操作符号) %%百分号标记 %c字 ...

  2. RNN(3) ------ “blog:RNN学习之路”

    博客链接:http://blog.csdn.net/yangyangyang20092010/article/details/50374289 Recurrent Neural Network 学习之 ...

  3. 写好shell脚本的13个技巧【转】

    有多少次,你运行./script.sh,然后输出一些东西,但却不知道它刚刚都做了些什么.这是一种很糟糕的脚本用户体验.我将在这篇文章中介绍如何写出具有良好开发者体验的 shell 脚本. 产品的最终用 ...

  4. Bootstrap3.0学习第一轮(入门)

    详情请查看 http://aehyok.com/Blog/Detail/7.html 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok 本文文章链接:ht ...

  5. Win10 + Visual Studio 2017 下 OpenCV无法显示图像的问题

    测试代码如下: #include "stdafx.h" #include<opencv2\opencv.hpp> #include<opencv2\highgui ...

  6. 通达OA批量处理没有结束但前台显示已经结束的流程

    问题描述: 通达OA系统出现大量流程没有结束,系统显示结束的问题 通过查询操作系统日志,数据库日志,包括程序日志没有发现异常,通过观察发现大量的流程结束时间都是在2016-02-16 17:32:XX ...

  7. 转载:2.2.2 配置项的语法格式《深入理解Nginx》(陶辉)

    原文:https://book.2cto.com/201304/19627.html 从上文的示例可以看出,最基本的配置项语法格式如下: 配置项名 配置项值1 配置项值2 - ; 下面解释一下配置项的 ...

  8. 在chrome开发者工具中观察函数调用栈、作用域链与闭包

    在chrome开发者工具中观察函数调用栈.作用域链与闭包 在chrome的开发者工具中,通过断点调试,我们能够非常方便的一步一步的观察JavaScript的执行过程,直观感知函数调用栈,作用域链,变量 ...

  9. JFreeChart入门

    JFreeChart主要用来各种各样的图表,这些图表包括:饼图.柱状图(普通柱状图以及堆栈柱状图).线图.区域图.分布图.混合图.甘特图以及一些仪表盘等等 (源代码下载) 示例程序运用的jar包: j ...

  10. CentOS切换为iptables防火墙并进行相关配置

    CentOS切换为iptables防火墙 切换到iptables首先应该关掉默认的firewalld,然后安装iptables服务. 1.关闭firewall: service firewalld s ...