题解:

树套树

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的更多相关文章

  1. BZOJ3946 : 无聊的游戏

    首先把所有串拼起来,后插入的串在前面,得到一个大串. 那么任意时刻,每个串是由这个大串的若干个不相交的子串从左到右拼接而成. 用线段树维护每个串,每个节点维护一个标记,表示区间内的串要加上什么前缀. ...

  2. [学习笔记]可持久化数据结构——数组、并查集、平衡树、Trie树

    可持久化:支持查询历史版本和在历史版本上修改 可持久化数组 主席树做即可. [模板]可持久化数组(可持久化线段树/平衡树) 可持久化并查集 可持久化并查集 主席树做即可. 要按秩合并.(路径压缩每次建 ...

随机推荐

  1. FTP-FileZilla

    服务器上安装FileZilla Server连接时报You appear to be behind a NAT router. Please configure the passive mode se ...

  2. luoguP2572 [SCOI2010]序列操作

    题目&&链接 反正数据都是一样的,luogu比较友好 luogu bzoj lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变 ...

  3. 海量数据处理-BitMap算法

    一.概述 本文将讲述Bit-Map算法的相关原理,Bit-Map算法的一些利用场景,例如BitMap解决海量数据寻找重复.判断个别元素是否在海量数据当中等问题.最后说说BitMap的特点已经在各个场景 ...

  4. C++中find()函数和rfind()函数的用法

    本文转载自http://blog.csdn.net/youxin2012/article/details/9162415 string中 find()的应用  (rfind() 类似,只是从反向查找) ...

  5. npm教程_脚手架原理以及bootstrap引入

    格式:vue init <templateName> <ProjectName> 例子:vue init webpack vue02 运行上面的命令后,脚手架帮忙按照webpa ...

  6. FOJ-1058-粗心的物理学家

    题目:粗心的物理学家 代码: #include<stdlib.h> #include<iostream> #include<cstdio> using namesp ...

  7. UVa 116 单向TSP(多段图最短路)

    https://cn.vjudge.net/problem/UVA-116 题意:给出m行n列的整数矩阵,从第一列任何一个位置出发每次往右,右上或右下走一格,最终到达最后一列,要求经过的整数之和最小. ...

  8. c++ 算法 栅格中两点之间连线

    屏幕划线,通过平面坐标系实现,基本组成是一个一个的点,起点为A,终点为B 本文的算法,可以实现平面栅格中,指定的A,B两点之间进行连线(代码中仅打印了两点间需要画出的坐标点) #include < ...

  9. DB中字段为null,为空,为空字符串,为空格要怎么过滤取出有效值

      比如要求取出微信绑定的,没有解绑的 未绑定,指定字段为null 绑定的,指定字段为某个字符串 解绑的,有的客户用的是更新指定字段为1,有的客户更新指定字段为‘1’ 脏数据的存在,比如该字段为空字符 ...

  10. visudo使用笔记

    目录前言一.介绍二.配置文件简介三.实战配置 前言:    su 的确为管理带来方便,通过切换到root下,能完成所有系统管理工具,只要把root的密码交给任何一个普通用户,他都能切换到root来完成 ...