BZOJ2555——SubString
0、题目很短,就不概括了
给你一个字符串init,要求你支持两个操作
(1):在当前字符串的后面插入一个字符串
(2):询问字符串s在当前字符串中出现了几次?(作为连续子串)
你必须在线支持这些操作。
1、分析:
a)首先我们来看40%的数据,我们可以建立后缀自动机,对于插入串,我们直接暴力的insert,不要怂。。。询问的话我们就在后缀自动机上的tranc上面跑。。然后暴力的统计right集合的数量,遍历这个子树,因为每一次都会更新,所以不能预处理,然后40pts get,时间复杂度O(n2)
b)也就是正解,我们发现a)做法的主要复杂度在于询问,询问就是求right集合,那么我们怎么快速的维护right集合呢?我们发现这个东西是个树,然后动态加点,然后维护子树和,是不是想到了lct呢?然而lct并不能维护子树的信息,是不是想到了toptree….然而toptree我都不会,怎么能写呢?所以吴大爷说这个是简单版的toptree,其实是lct…..然后我们在lct中多维护一个信息。这个信息表示他的所有虚儿子的子树的和。。那么access的时候我们要更新一下这个信息,还有link操作,为什么link操作也要维护呢?因为link操作连的虚边QAQ….然后每次询问我们可以直接依旧在后缀自动机里面一点一点的向下走,然后我们再查询这个子树,查询之前我们要Access一下,这样答案就是这个点的虚儿子+这个点是不是后缀咯。。。有个问题就是在getans的函数中我把return的p -> vs + p -> w改成p -> s就wa了QAQ,跪求神犇帮我看看
时间复杂度O(nlogn+n)
#include <stack>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
inline int read(){
char ch = getchar(); int x = 0, f = 1;
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;
ch = getchar();
}
while('0' <= ch && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
namespace LCT{
struct Node{
Node *ch[2], *fa;
int s, vs, w;
bool rev;
Node();
inline int which();
inline void reverse(){
if(this){
rev ^= 1;
swap(ch[0], ch[1]);
}
}
inline void pd(){
if(rev){
ch[0] -> reverse();
ch[1] -> reverse();
rev = false;
}
}
inline void maintain(){
s = vs + w + ch[0] -> s + ch[1] -> s;
}
} *null = new Node, tree[1500010], *pos[1500010];
int tot;
Node::Node(){
rev = false;
s = vs = w = 0;
ch[0] = ch[1] = fa = null;
}
inline int Node::which(){
if(fa == null || (this != fa -> ch[0] && this != fa -> ch[1])) return -1;
return this == fa -> ch[1];
}
inline void rotate(Node *o){
Node *p = o -> fa;
int l = o -> which(), r = l ^ 1;
o -> fa = p -> fa;
if(p -> which() != -1) p -> fa -> ch[p -> which()] = o;
p -> ch[l] = o -> ch[r];
if(o -> ch[r]) o -> ch[r] -> fa = p;
o -> ch[r] = p; p -> fa = o;
o -> ch[r] -> maintain();
o -> maintain();
}
inline void splay(Node *o){
static stack<Node*> st;
if(!o) return;
Node *p = o;
while(1){
st.push(p);
if(p -> which() == -1) break;
p = p -> fa;
}
while(!st.empty()){
st.top() -> pd(); st.pop();
}
while(o -> which() != -1){
p = o -> fa;
if(p -> which() != -1){
if(p -> which() ^ o -> which()) rotate(o);
else rotate(p);
}
rotate(o);
}
}
inline void Access(Node *o){
Node *y = null;
while(o != null){
// if(o == NULL) printf("fuck\n");
splay(o);
// printf("%d\n", o -> vs);
o -> vs += o -> ch[1] -> s;
o -> ch[1] = y;
o -> vs -= y -> s;
o -> maintain();
y = o; o = o -> fa;
}
}
inline void MovetoRoot(Node *o){
Access(o);
splay(o);
o -> reverse();
}
inline Node* FindRoot(Node *o){
Access(o);
splay(o);
while(o -> ch[0] != null) o = o -> ch[0];
return o;
}
inline void Link(Node *x, Node *y){
MovetoRoot(x);
x -> fa = y;
y -> vs += x -> s;
y -> maintain();
}
inline void Cut(Node *x, Node *y){
MovetoRoot(x);
Access(y);
splay(y);
y -> ch[0] = x -> fa = null;
y -> maintain();
}
}
using namespace LCT;
struct node{
int tranc[27], len, fa;
} a[1500010];
int p, q, np, nq;
int tail, cnt;
inline void insert(int k){
p = tail;
pos[cnt + 1] = &tree[cnt + 1];
np = ++ cnt;
a[np].len = a[tail].len + 1;
for(p = tail; p && !a[p].tranc[k]; p = a[p].fa) a[p].tranc[k] = np;
if(!p) a[np].fa = 1;
else{
q = a[p].tranc[k];
if(a[q].len == a[p].len + 1) a[np].fa = q;
else{
pos[cnt + 1] = &tree[cnt + 1];
a[nq = ++ cnt] = a[q];
a[q].fa = a[np].fa = nq;
a[nq].len = a[p].len + 1;
for(; a[p].tranc[k] == q; p = a[p].fa) a[p].tranc[k] = nq;
Cut(pos[q], pos[a[nq].fa]);
Link(pos[nq], pos[a[nq].fa]);
Link(pos[nq], pos[q]);
}
}
tail = np;
pos[np] -> fa = pos[a[np].fa];
MovetoRoot(pos[np]);
// printf("%d\n", k);
pos[np] -> w = 1;
pos[np] -> maintain();
}
inline int getans(Node *p){
MovetoRoot(pos[1]);
Access(p);
return p -> w + p -> vs;
}
char s[1000010];
int main(){
null -> ch[0] = null -> ch[1] = null -> fa = NULL;
null -> vs = null -> s = null -> w = 0;
null -> rev = false;
int n = read();
scanf("%s", s + 1);
int len = strlen(s + 1);
cnt = tail = 1;
pos[1] = &tree[1]; tot = 1;
for(int i = 1; i <= len; i ++) insert(s[i] - 'A' + 1);
char qt[10];
int mask = 0;
for(int i = 1; i <= n; i ++){
scanf("%s%s", qt, s);
int len = strlen(s);
for(int _mask = mask, j = 0; j < len; j ++){
_mask = (_mask * 131 + j) % len;
swap(s[_mask], s[j]);
}
if(qt[0] == 'A'){
// tail = 1;
for(int j = 0; j < len; j ++) insert(s[j] - 'A' + 1);
}
else{
int t = 1, ans = 0;
for(int j = 0; j < len; j ++){
t = a[t].tranc[s[j] - 'A' + 1];
}
if(t) ans = getans(pos[t]);
printf("%d\n", ans);
mask ^= ans;
}
}
return 0;
}
BZOJ2555——SubString的更多相关文章
- BZOJ2555 SubString【SAM + Link Cut Tree】
BZOJ2555. SubString 要求在线询问一个串在原串中出现的次数,并且可以在原串末尾添加字符串 如果没有修改的话,考虑建出\(parent\)树之后统计每个\(endpos\)节点的\(r ...
- [BZOJ2555]SubString LCT+后缀自动机
2555: SubString Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 3253 Solved: 975[Submit][Status][Di ...
- bzoj2555: SubString
SAM+LCT维护parent tree版本 虽然说子树维护那套理论需要ETT 不过parent tree的根是固定的,所以用lct加一些奇怪的乱搞就行了 //随手拖个SAM的板子和LCT的板子,然后 ...
- 2019.03.01 bzoj2555: SubString(sam+lct)
传送门 题意简述: 要求在线支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 思路: 考虑用lctlctlct来动态维护samsa ...
- bzoj2555: SubString sam+lct
题意:懒得写背景了,给你一个字符串init,要求你支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 你必须在线支持这些操作. 题解 ...
- bzoj2555 substring(LCT 后缀自动机)
/* 动态求right集合的大小 LCT维护parent树即可 注意 由于树是有向的不会换根并且每次操作单一, 于是不需要维护子树和(写起来很麻烦) 直接打标记修改即可 */ #include< ...
- bzoj千题计划285:bzoj2555: SubString
http://www.lydsy.com/JudgeOnline/problem.php?id=2555 后缀自动机,用LCT维护parent树 一个串的出现次数 = parent 树 上 其所在状态 ...
- BZOJ2555 SubString【后缀自动机+LCT】
Description 懒得写背景了,给你一个字符串init,要求你支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 你必须在线支 ...
- luogu5212/bzoj2555 substring(后缀自动机+动态树)
对字符串构建一个后缀自动机. 每次查询的就是在转移边上得到节点的parent树中后缀节点数量. 由于强制在线,可以用动态树维护后缀自动机parent树的子树和. 注意一个玄学的优化:每次在执行连边操作 ...
随机推荐
- JavaWeb---总结(十五)JSP基础语法
一.JSP模版元素 JSP页面中的HTML内容称之为JSP模版元素. JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观. 二.JSP表达式 JSP脚本表达式(expression)用于将 ...
- ubuntu默认防火墙
ubuntu 9.10默认的是UFW防火墙,已经支持界面操作了.在命令行运行ufw命令就可以看到提示的一系列可进行的操作. 最简单的一个操作:sudo ufw status可检查防火墙的状态,我的返回 ...
- uC/OS-II邮箱(mbox)块
/*************************************************************************************************** ...
- Dijkstra最短路径算法实例
#include <stdio.h>#include <stdlib.h>/* Dijkstra算法 */#define VNUM 5#define MV 65536int P ...
- glade2支持C++代码的输出(3)
今天完成了glade-2生成configure.ac/Makefile.am等调整 代码为:cpp_out_4.patch.zip BaseObject类也做了一些小的调整:BaseObject.00 ...
- php 如何造一个简短原始的数据库类用来增加工作效率
class DBDA{ public $host="localhost"; public $uid="root"; public $pwd="123& ...
- HTTPS 客户端验证 服务端证书流程
网上的文章很多, 但是对摘要的验证流程不够通俗易懂. QQ截图20160420114804.png 证书预置和申请 1:客户端浏览器会预置根证书, 里面包含CA公钥2:服务器去CA申请一个证书3: C ...
- jQuery实现表格行的动态增加与删除 序号 从 1开始排列
<table id="tab" border="1" width="60%" align="center" sty ...
- git push to nas
1 建nas目录 在nas的/volume1/git_repos目录下新建相关的目录,并在该目录下运行git init --bare cd /volume1/git_repos mkdir wifi_ ...
- MySQL数据库常用函数
一.数学函数 数学函数主要用于处理数字,包括整型.浮点数等. ABS(x) 返回x的绝对值 不区分大小写 SELECT ABS(-1) -- 返回1 CEIL(x),CEILING(x) 返回大于或等 ...