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 $ ...
随机推荐
- Linux用户组相关指令
⒈增加用户组 ①groupadd 用户组名 ⒉删除用户组 ①groupdel 用户组名 ⒊修改用户所在的用户组 ①usermod -g 用户组 用户名 ★用户和用户组的相关文件 ①/etc/passw ...
- STM32F103X datasheet学习笔记---Flexible static memory controller (FSMC)
1.前言 FSMC模块能够与同步或异步存储器和16位PC存储器卡接口,它的主要作用是: 将AHB传输信号转换到适当的外部设备协议 满足访问外部设备的时序要求 所有的外部存储器共享控制器输出的地址.数据 ...
- Oracle11g的database 和client的区别是什么?
由于工作需要,刚开始接触oracle数据库,完全小白,下载的时候看到有database和client两种类型可供下载,一时不知如何是好,于是网上询问得知其中区别,在此记录一下自己的无知. “datab ...
- 【转】CString与string、char*的区别和转换
我们在C++的开发中经常会碰到string.char*以及CString,这三种都表示字符串类型,有很多相似又不同的地方,常常让人混淆.下面详细介绍这三者的区别.联系和转换: 各自的区别 char*: ...
- hdu 4348 To the moon (主席树区间更新)
传送门 题意: 一个长度为n的数组,4种操作 : (1)C l r d:区间[l,r]中的数都加1,同时当前的时间戳加1 . (2)Q l r:查询当前时间戳区间[l,r]中所有数的和 . (3)H ...
- 005_关于HTTP协议中的保持连接
缘起 中午在群里讨论,用ab测试 一台只提供静态文件服务, 不与其他任何系统交互的时候,为什么也会产生大量的TIME WAIT状态的. 首先,我们可以简单的理解,在TCP连接的两端,谁主动断开连接(先 ...
- 03-MySql安装和基本管理
本节掌握内容: MySQL的介绍安装.启动 windows上制作服务 MySQL破解密码 MySQL中统一字符编码 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 O ...
- Mess it up!搞乱代码
Mess it up! 作者: Laruence( ) 本文地址: http://www.laruence.com/2009/01/07/656.html 转载请注明出处 恩,这段代码还能再乱点, ...
- java 语言的主要特点
java 语言主要特点如下: 1:简单 2:面向对象 3:分布性 4:可移植性 5:安全性 6:健壮性 二:java 主要术语 三:java 核心是面向对象程序设计OOP 四:封装 五:多态 六:继承 ...
- java多线程快速入门(三)
通过实现Runnable接口实现多线程 package com.cppdy; //通过实现Runnable接口实现多线程 class MyThread1 implements Runnable{ @O ...