bzoj3946
题解:
树套树
treap+线段树
treap就把线段树上的节点弄一下
然后修改的时候
把中间的一段一起加
把两头重新计算(二分+hash)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef unsigned int U;
const int N=;
int n,m,x,len,cur,st[N],en[N],q[N][],stq[N];
int pre[N],y[N],h[N],T[N],tag[N],enq[N];
U po[N];
char op[],s[N],pool[N],str[N];
namespace DS
{
const int N=;
int tot,l[N],r[N],v[N];
U f[N];
void up(int x)
{
v[x]=v[l[x]]+v[r[x]];
f[x]=f[l[x]]*po[v[r[x]]]+f[r[x]];
}
int build(int a,int b,int c,int d)
{
int x=++tot;
if (a==b)
{
v[x]=;
f[x]=str[a];
return x;
}
int mid=(a+b)/;
if (c<=mid)l[x]=build(a,mid,c,d);
if (d>mid)r[x]=build(mid+,b,c,d);
up(x);
return x;
}
int merge(int x,int y,int a,int b)
{
if (!x||!y)return x+y;
int z=++tot,mid=(a+b)/;
l[z]=merge(l[x],l[y],a,mid);
r[z]=merge(r[x],r[y],mid+,b);
up(z);
return z;
}
U hash(int x,int k)
{
int a=,b=cur,mid,t;
U h=;
while (a<b)
{
mid=(a+b)/;
t=v[l[x]];
if (k<=t)
{
b=mid;
x=l[x];
}
else
{
h=h*po[t]+f[l[x]];
k-=t;
a=mid+;
x=r[x];
}
}
return h*po[v[x]]+f[x];
}
}
void ins(int x,int p)
{
pre[x]=DS::merge(pre[x],p,,cur);
}
void down(int x)
{
if (pre[x])
{
ins(x<<,pre[x]);
ins(x<<|,pre[x]);
pre[x]=;
}
}
void push(int x,int a,int b,int c,int d,int p)
{
if (c<=a&&b<=d)
{
ins(x,p);
return;
}
down(x);
int mid=(a+b)/;
if (c<=mid)push(x<<,a,mid,c,d,p);
if (d>mid)push(x<<|,mid+,b,c,d,p);
}
void root(int x,int a,int b,int c)
{
if (a==b)
{
T[a]=DS::merge(T[a],pre[x],,cur);
pre[x]=;
return;
}
down(x);
int mid=(a+b)/;
if (c<=mid)root(x<<,a,mid,c);
else root(x<<|,mid+,b,c);
}
int lcp(int x,int y)
{
root(,,n,x);
root(,,n,y);
x=T[x];y=T[y];
int l=,r=min(DS::v[x],DS::v[y]),t=;
while (l<=r)
{
int mid=(l+r)/;
if (DS::hash(x,mid)==DS::hash(y,mid))l=(t=mid)+;
else r=mid-;
}
return t;
}
void tag1(int x,int p)
{
h[x]+=p;
tag[x]+=p;
}
void pb(int x)
{
if (tag[x])
{
tag1(x<<,tag[x]);
tag1(x<<|,tag[x]);
tag[x]=;
}
}
void up(int x)
{
h[x]=min(h[x<<],h[x<<|]);
}
void build(int x,int a,int b)
{
if (a==b)
{
h[x]=lcp(a,a+);
return;
}
int mid=(a+b)/;
build(x<<,a,mid);
build(x<<|,mid+,b);
up(x);
}
void cal(int x,int a,int b,int c)
{
if (a==b)
{
h[x]=lcp(a,a+);
return;
}
pb(x);
int mid=(a+b)/;
if (c<=mid)cal(x<<,a,mid,c);
else cal(x<<|,mid+,b,c);
up(x);
}
void change(int x,int a,int b,int c,int d,int p)
{
if (c<=a&&b<=d)
{
tag1(x,p);
return;
}
pb(x);
int mid=(a+b)/;
if (c<=mid)change(x<<,a,mid,c,d,p);
if (d>mid)change(x<<|,mid+,b,c,d,p);
up(x);
}
int ask(int x,int a,int b,int c,int d)
{
if (c<=a&&b<=d)return h[x];
pb(x);
int mid=(a+b)/,t=N;
if (c<=mid)t=ask(x<<,a,mid,c,d);
if (d>mid)t=min(t,ask(x<<|,mid+,b,c,d));
return t;
}
void makepush(int l,int r,int A,int B)
{
push(,,n,l,r,DS::build(,cur,A,B));
if (l<r)change(,,n-,l,r-,B-A+);
if (l>)cal(,,n-,l-);
if (r<n)cal(,,n-,r);
}
int query(int l,int r)
{
if (l==r)return lcp(l,l);
return ask(,,n-,l,r-);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=;i<=n;i++)
{
scanf("%s",s);
len=strlen(s);
st[i]=cur+;
for (int j=;j<len;j++)pool[++cur]=s[j];
en[i]=cur;
}
for (int i=;i<=m;i++)
{
scanf("%s%d%d",op,&q[i][],&q[i][]);
if (op[]=='Q')q[i][]=;
else
{
scanf("%s",s);
len=strlen(s);
stq[i]=cur+;
for (int j=;j<len;j++)pool[++cur]=s[j];
enq[i]=cur;
}
}
int cur=;
for (int i=m;i;i--)
if (!q[i][])
{
x=cur+;
for (int j=stq[i];j<=enq[i];j++)str[++cur]=pool[j];
stq[i]=x;
enq[i]=cur;
}
for (int i=;i<=n;i++)
{
x=cur+;
for (int j=st[i];j<=en[i];j++)str[++cur]=pool[j];
st[i]=x;en[i]=cur;
}
po[]=;
for (int i=;i<=cur;i++)po[i]=po[i-]*;
for (int i=;i<=n;i++)T[i]=DS::build(,cur,st[i],en[i]);
build(,,n-);
for (int i=;i<=m;i++)
if (q[i][])printf("%d\n",query(q[i][],q[i][]));
else makepush(q[i][],q[i][],stq[i],enq[i]);
}
bzoj3946的更多相关文章
- BZOJ3946 : 无聊的游戏
首先把所有串拼起来,后插入的串在前面,得到一个大串. 那么任意时刻,每个串是由这个大串的若干个不相交的子串从左到右拼接而成. 用线段树维护每个串,每个节点维护一个标记,表示区间内的串要加上什么前缀. ...
- [学习笔记]可持久化数据结构——数组、并查集、平衡树、Trie树
可持久化:支持查询历史版本和在历史版本上修改 可持久化数组 主席树做即可. [模板]可持久化数组(可持久化线段树/平衡树) 可持久化并查集 可持久化并查集 主席树做即可. 要按秩合并.(路径压缩每次建 ...
随机推荐
- 0xc0000005:读取位置时发生访问冲突
这是空指针,比如: A* a=NULL; a->fun();//会提示标题错误,因为a没有分配空间
- Python3基础 try-except-finally 的简单示例
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- 在terminal下的快捷键
1.回到行首的快捷键:ctrl + esc 2.ctrl+[可以替代esc
- 【附8】zipkin
一.zipkin作用 全链路追踪工具(查看依赖关系) 查看每个接口.每个service的执行速度(定位问题发生点或者寻找性能瓶颈) 二.zipkin工作原理 创造一些追踪标识符(tracingId,s ...
- 浅谈java中死锁问题
知识点:死锁的产生.死锁的实例 一:死锁的产生 我们在解决多线程共享资源的线程同步问题时,会使用synchronized关键字修饰方法或者通过Lock加锁方式修饰方法.代码块,防止多个线程访问统一资源 ...
- HDU 2222 Keywords Search(AC自动机模板题)
http://acm.hdu.edu.cn/showproblem.php?pid=2222 题意:给出多个单词,最后再给出一个模式串,求在该模式串中包含了多少个单词. 思路: AC自动机的模板题. ...
- telnet不是内部或外部命令的解决方案
telnet主要是为了维护使用,windows默认不打开这个功能,所有无法登陆 按照下面截图,可轻松打开telnet功能 点击控制面板 选择程序和功能 点击左上角打开 turn windows fea ...
- Python day16 tag式整体退出技巧
在写一些服务器时,往往会多层嵌套循环,可利用tag变量实现整体退出功能,代码: tag=True while tag: print('level1') choice=input('level1> ...
- MongoDB(课时12 字段判断)
3.4.2.7 判断某个字段是否存在 使用“$exists”可以判断某个字段是否存在,如果设置为true表示存在,false表示不存在. 范例:查询具有parents成员的数据 db.students ...
- MongoDB(课时10 数组)
3.4.2.5 数组查询 MongoDB里面支持数组保存,一旦支持数组保存,就需要对于数组的数据进行匹配. 范例:插入一部分数组内容 课程是数组的形式(截图时少截一条信息) 此时数据包含数组内容,而后 ...