P5350 序列

题意

维护一个序列,支持区间求和、赋值、加值、复制、交换、翻转操作,其中交换和复制操作保证两段区间长度相等且不交。答案对 \(1e9+7\) 取模。

思路

对于区间求和、赋值、加值、交换、翻转操作我们都可以很轻松地使用平衡树进行维护。所以现在的难点就在于复制操作:如何复制一段区间?

如果我们暴力复制的话,每次我们不得不将被复制的子树扫一遍进行复制,这是肯定不行的。

于是我们使用可持久化平衡树。其中心思想就是每次修改一个节点的信息时,将该节点复制一遍。这样我们在进行复制操作的时候就可以复制出来一个新的树而不会对原树有影响,而且因为不是每次都遍历子树,所以时间复杂度正确。

但是因为要丢弃之前的节点所以空间复杂度略微有些大。因为我们可以进行垃圾回收定期重构使得空间被合理重复利用。

于是这道题就解决了。我使用了 FHQ treap 进行实现,因为发现对于这些操作 FHQ 会比较方便。

然后这道题不卡 ODT 但卡复杂度保证的写法。

细节和我犯过的错误

  • 这是个定长的序列,所以我们每次重构的时候可以选择使用构建二叉搜索树的方法线性构建,否者会被卡常。

  • 每次更改节点信息时都要进行复制pushdown,merge,split 函数和修改操作里都要复制。

  • 注意 pushdownclone 的前后顺序。有时候我们并不需要将原节点进行下传标记以免建出无用节点增大常数。

  • 注意传参时用的是哪个节点的参数。我曾在 split 操作中下传原节点的儿子,实际上是复制后的节点的儿子。

  • FHQ 在新建节点后的 rand 值占空间,我们用一段话在 merge 的时候现场随机,即:

    rd(0,(e[a].siz+e[b].siz)-1)<e[a].siz

    可以省下一点空间。

  • 复制和交换的时候记住,给出的区间端点位置可不保证升序的。

  • 重构之后再清空节点数,因为在遍历搜索树的时候会 pushdown 而新加节点。

代码

这里的代码是加强版的代码。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
#include<chrono>
#include<random>
using namespace std;
inline int read(){
int w=0,x=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=x*10+(c^48),c=getchar();
return w?-x:x;
}
char buf[1 << 21], a[20]; int p, p2 = -1;
inline void flush() {
fwrite(buf, 1, p2 + 1, stdout);
p2 = -1;
}
inline void print(int x) {
if (p2 > 1 << 20) flush();
if (x < 0) buf[++p2] = 45, x = -x;
do a[++p] = x % 10 + 48;while (x /= 10);
do buf[++p2] = a[p];while (--p);
}
namespace star
{
const int maxn=3e5+10,maxm=8e6+10,mod=1e9+7;
mt19937 rnd(std::chrono::system_clock::now().time_since_epoch().count());
int rd(int l,int r){return std::uniform_int_distribution<int>(l,r)(rnd);}
int n,m,a[maxn];
int lastans;
struct FHQ{
#define ls e[ro].son[0]
#define rs e[ro].son[1]
struct node{
int son[2],siz,tag,add,val,sum;
bool rev;
}e[maxm];
int tot,rt;
FHQ():e(),tot(0),rt(0){}
inline void clone(int &x){e[++tot]=e[x],x=tot;}
inline int newnode(const int &a){return e[++tot]=(node){{0,0},1,-1,0,a,a,false},tot;}
inline void pushup(const int &ro){e[ro].siz=e[ls].siz+e[rs].siz+1,e[ro].sum=(1ll*e[ls].sum+e[rs].sum+e[ro].val)%mod;}
void rev(const int &ro){if(ro)e[ro].rev^=1,swap(ls,rs);}
void add(const int &ro,const int &v){if(ro)e[ro].val=(e[ro].val+v)%mod,e[ro].sum=(e[ro].sum+1ll*e[ro].siz*v)%mod,e[ro].add=(e[ro].add+v)%mod;}
void assign(const int &ro,const int &v){if(ro)e[ro].val=v,e[ro].sum=1ll*e[ro].siz*v%mod,e[ro].add=0,e[ro].tag=v;}
inline void pushdown(const int &ro){
if(!e[ro].rev and e[ro].tag==-1 and !e[ro].add) return;
if(ls) clone(ls);if(rs) clone(rs);
if(e[ro].rev) rev(ls),rev(rs),e[ro].rev=false;
if(e[ro].tag!=-1) assign(ls,e[ro].tag),assign(rs,e[ro].tag),e[ro].tag=-1;
if(e[ro].add) add(ls,e[ro].add),add(rs,e[ro].add),e[ro].add=0;
}
int build(const int &l=1,const int &r=n){
if(l>r)return 0;
int mid=(l+r)>>1;
int ro=newnode(a[mid]);
ls=build(l,mid-1),rs=build(mid+1,r);
pushup(ro);
return ro;
}
int merge(int a,int b){
if(!a or !b)return a|b;
if(rd(0,(e[a].siz+e[b].siz)-1)<e[a].siz){
clone(a),pushdown(a);
e[a].son[1]=merge(e[a].son[1],b);
pushup(a);return a;
}else{
clone(b),pushdown(b);
e[b].son[0]=merge(a,e[b].son[0]);
pushup(b);return b;
}
}
void split(int ro,int k,int &a,int &b){
if(!ro) return a=b=0,void();
if(e[ls].siz<k) a=ro,clone(a),pushdown(a),split(e[a].son[1],k-e[e[a].son[0]].siz-1,e[a].son[1],b),pushup(a);
else b=ro,clone(b),pushdown(b),split(e[b].son[0],k,a,e[b].son[0]),pushup(b);
}
inline void copy(){
int l1=read()^lastans,r1=read()^lastans,l2=read()^lastans,r2=read()^lastans,a,b,c,d,e;int bk=1;
if(r1>r2)swap(l1,l2),swap(r1,r2),bk=0;
split(rt,r2,d,e);split(d,l2-1,c,d);split(c,r1,b,c);split(b,l1-1,a,b);
if(bk) rt=merge(a,merge(b,merge(c,merge(b,e))));
else rt=merge(a,merge(d,merge(c,merge(d,e))));
}
inline void Swap(){
int l1=read()^lastans,r1=read()^lastans,l2=read()^lastans,r2=read()^lastans,a,b,c,d,e;
if(r1>r2)swap(l1,l2),swap(r1,r2);
split(rt,r2,d,e);split(d,l2-1,c,d);split(c,r1,b,c);split(b,l1-1,a,b);
rt=merge(a,merge(d,merge(c,merge(b,e))));
}
inline void push(int ro){
if(!ro)return;
pushdown(ro);
push(ls),a[++n]=e[ro].val,push(rs);
}
#undef ls
#undef rs
}S;
inline void work(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
S.rt=S.build();
while(m--){
switch(read()){
case 1:{
int l=read()^lastans,r=read()^lastans,a,b,c;
S.split(S.rt,r,b,c);S.split(b,l-1,a,b);
printf("%d\n",lastans=S.e[b].sum);
S.rt=S.merge(a,S.merge(b,c));
break;
}
case 2:{
int l=read()^lastans,r=read()^lastans,a,b,c;
S.split(S.rt,r,b,c);S.split(b,l-1,a,b);
S.clone(b);
S.assign(b,read()^lastans);
S.rt=S.merge(a,S.merge(b,c));
break;
}
case 3:{
int l=read()^lastans,r=read()^lastans,a,b,c;
S.split(S.rt,r,b,c);S.split(b,l-1,a,b);
S.clone(b);
S.add(b,read()^lastans);
S.rt=S.merge(a,S.merge(b,c));
break;
}
case 4:S.copy();break;
case 5:S.Swap();break;
case 6:{
int l=read()^lastans,r=read()^lastans,a,b,c;
S.split(S.rt,r,b,c);S.split(b,l-1,a,b);
S.clone(b);
S.rev(b);
S.rt=S.merge(a,S.merge(b,c));
break;
}
}
if(S.tot>6500000) n=0,S.push(S.rt),S.rt=S.tot=0,S.rt=S.build();
}
n=0,S.push(S.rt);
for(int i=1;i<=n;i++) printf("%d ",a[i]);
}
}
signed main(){
star::work();
flush();
return 0;
}

P5350 序列的更多相关文章

  1. 洛谷 P5350 序列 珂朵莉树

    题目描述 分析 操作一.二.三为珂朵莉树的基本操作,操作四.五.六稍作转化即可 不会珂朵莉树请移步至这里 求和操作 把每一段区间分别取出,暴力相加 ll qh(ll l,ll r){ it2=Spli ...

  2. ODT珂朵莉树

    关于ODT,据说是毒瘤lxl发明的,然后毒瘤鱼鱼因为我用ODT误导人D了我一回-- 这是一种基于 \(set\) 的暴力数据结构. 在使用时请注意,没看见这2东西千万别用-- 1.保证数据随机 2.有 ...

  3. 【夯实PHP基础】UML序列图总结

    原文地址 序列图主要用于展示对象之间交互的顺序. 序列图将交互关系表示为一个二维图.纵向是时间轴,时间沿竖线向下延伸.横向轴代表了在协作中各独立对象的类元角色.类元角色用生命线表示.当对象存在时,角色 ...

  4. Windows10-UWP中设备序列显示不同XAML的三种方式[3]

    阅读目录: 概述 DeviceFamily-Type文件夹 DeviceFamily-Type扩展 InitializeComponent重载 结论 概述 Windows10-UWP(Universa ...

  5. 软件工程里的UML序列图的概念和总结

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习! 软件工程的一般开发过程:愿景分析.业务建模,需求分析,健壮性设计,关键设计,最终设计,实现…… 时序图也叫序列图(交互图),属于软件 ...

  6. python序列,字典备忘

    初识python备忘: 序列:列表,字符串,元组len(d),d[id],del d[id],data in d函数:cmp(x,y),len(seq),list(seq)根据字符串创建列表,max( ...

  7. BZOJ 1251: 序列终结者 [splay]

    1251: 序列终结者 Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 3778  Solved: 1583[Submit][Status][Discu ...

  8. 最长不下降序列nlogn算法

    显然n方算法在比赛中是没有什么用的(不会这么容易就过的),所以nlogn的算法尤为重要. 分析: 开2个数组,一个a记原数,f[k]表示长度为f的不下降子序列末尾元素的最小值,tot表示当前已知的最长 ...

  9. [LeetCode] Sequence Reconstruction 序列重建

    Check whether the original sequence org can be uniquely reconstructed from the sequences in seqs. Th ...

随机推荐

  1. python_request的安装及模拟json的post请求及带参数的get请求

    一.Requests模块安装 安装方式一:执行 pip install -U requests 联网安装requests 安装方式二:进入https://pypi.org/project/reques ...

  2. 实战SpringBoot Admin

    长话短说哦,直接查看下文吧 目录 声明 先锋 前提 SpringBoot Admin 介绍 服务端的搭建 客户端的搭建 参数的指南 尾声 声明 见名知意,实战SpringBoot Admin,实战!实 ...

  3. 重新整理 .net core 实践篇—————中间件[十九]

    前言 简单介绍一下.net core的中间件. 正文 官方文档已经给出了中间件的概念图: 和其密切相关的是下面这两个东西: IApplicationBuilder 和 RequestDelegate( ...

  4. Spring Boot 实战:如何自定义 Servlet Filter

    1.前言 有些时候我们需要在 Spring Boot Servlet Web 应用中声明一些自定义的 Servlet Filter来处理一些逻辑.比如简单的权限系统.请求头过滤.防止 XSS 攻击等. ...

  5. 同事内推的那位Linux C/C++后端开发同学面试没过......

    最近同事内推了一位 Linux C/C++ 后端开发的同学到我们公司面试,我是一面的面试官,很遗憾这位工作了两年的同学面试表现不是很好.我问了如下一些问题: "redis持久化机制,redi ...

  6. UV贴图类型

      凹凸贴图Bump Map.法线贴图Normal Map.高度贴图Height map.漫反射贴图Diffuse Map.高光贴图Specular Map.AO贴图Ambient Occlusion ...

  7. Gitlab 定时备份

    要求 1.为了能够备份和恢复,请确保你的系统上安装了Rsync #Debian/Ubauntu sudo apt-get install rsync # RHEL/Centos sudo yum in ...

  8. 『心善渊』Selenium3.0基础 — 22、使用浏览器加载项配置实现用户免登陆

    目录 1.浏览器的加载项配置 2.加载Firefox配置 3.加载Chrome配置 1.浏览器的加载项配置 在很多情况下,我们在登录网站的时候,浏览器都会弹出一个是否保存登录账号的信息.如果我们选择保 ...

  9. 使用IDEA配置Maven

    IDEA中配置Maven File --> settings 推荐配置:设置maven在不联网的情况下使用本地插件 一般使用maven为我们提供好的骨架时,是需要联网的,配置这个,可以在没有网络 ...

  10. Java:java -jar命令讲解

    1. 当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出 #正常启动jar包 java -jar XXX.jar#当前ssh窗口被锁定,可按CTRL + C打断程序运行, ...