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. 阿里面试挂了,就因为面试官说我Spring 事务管理(器)不熟练?

    前言 事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一.但除了八股文中需要熟读并背诵的那些个传播行为之外,背后的"为什么"和核心原理更为重要. ​ 写这篇文章之前,我 ...

  3. Java Spring boot 多商户入驻 外卖|跑腿|代驾 Uniapp版本

    技术说明: 源码下载:https://www.yuanmahy.com/8357.html 开发环境:jdk1.8,mysql5.7,node 9.4,redis6.2,npm6.9 开发工具:前端使 ...

  4. 【NX二次开发】图标图像

    用户定义位图的目录位置的环境变量 UGII_BITMAP_PATH 在NX日志中查看NX图标需要设置的变量 变量名:PRINT_DIALOG_BITMAP_NAMES 变量值:1 查看系统图标的方法1 ...

  5. 深度解密:Java与线程的关系

    并发不一定要依赖多线程(如PHP的多进程并发),但在Java中谈论并发,大多数都与线程脱不开关系. 线程的实现 线程是CPU调度的基本单位,Thread类与大部分的Java API有显著的差别,它的所 ...

  6. 6.6考试总结(NOIP模拟4)

    前言 考试这种东西暴力拉满就对了QAQ T1 随 题解 解题思路 DP+矩阵乘(快速幂)+数论 又是一道与期望无关的期望题,显然答案是 总情况/情况数(\(n^m\)). 接下来的问题就是对于总情况的 ...

  7. 4.5 RNN循环神经网络(recurrent neural network)

     自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取: https://www.cnblogs.com/bclshuai/p/11380657.html 1.1  RNN循环神经网络 ...

  8. 双向链表(DoubleLinkList)

    双向链表 有关链表的知识可以点击我上篇文章这里就不再赘述LinkedList 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个 ...

  9. js 实时监听滚动条状态 判断滚动条位置

      var scrollFunc = function (e) {    e = e || window.event; var t = document.documentElement.scrollT ...

  10. Vue $refs无法操作element-ui组件

    比如我要操作这个dom元素↓↓↓ <el-badge :value="1" :max="99" class="message"> ...