国际惯例的题面:

考虑如果没有强制在线我们能怎么水掉这个题,先构造出字符串,各种方法求一下后缀数组,然后线段树维护区间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. JavaScript对象复制(二)

    <script> function copy(a) { ret = {}; for (sth in a) { temp = a[sth]; if (temp instanceof Arra ...

  2. 【CTF REVERSE】ctf02-查找字符串

    1.前言 公司大拿给写的一个CTF逆向程序,提升我们组内人员的水平. 基于对话框MFC框架开发,使用EDIT控制特性隐藏Flag,可借助spy4win之类窗体工具找出Flag. 程序加UPX壳,已对壳 ...

  3. fnmatch模块的使用

    fnmatch模块的使用 此模块的主要作用是文件名称的匹配,并且匹配的模式使用的unix shell风格.fnmatch比较简单就4个方法分别是:fnmatch,fnmatchcase,filter, ...

  4. python 读取文件时报错UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 205: illegal multib

    python 读取文件时报错UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 205: illegal multib ...

  5. IE中window的模态框与返回值

    window.returnValue是javascript中html的window对象的属性,目的是返回窗口值,当用window.showModalDialog函数打开一个IE的模态窗口时,用于返回窗 ...

  6. IDEA测试结果查看

    点击漏斗图标切换查看测试日志信息,点击,导出测试报告

  7. SQL日期时间和字符串函数

  8. java多线程快速入门(十六)

    ThreadLocal关键字实现每个线程有自己的变量 package com.cppdy; class Number { private int num; public static ThreadLo ...

  9. Jquery----属性的利用

    属性操作: 1.属性 属性(如果你的选择器选出了多个对象,那么默认只会返回出第一个属性). attr(属性名|属性值) - 一个参数是获取属性的值,两个参数是设置属性值 - 点击加载图片示例 remo ...

  10. MySQL学习笔记:一道group by+group_concat解决的小问题

    闲来无事,逛逛V2EX发现一道MySQL数据库题目,原题如下: 遂打开很长一段时间都没用过SQLyog,噗呲噗呲的干起活来…… 建测试表: CREATE TABLE test_001 ( id INT ...