题目

【题目描述】

有一个长度为 $n$ 的序列 $a_1, a_2, \dots, a_n$,一开始每个位置都是白色。如果一个区间中每个位置都是白色,则称这是一个白白的区间。如果一个白白的区间向左或向右延长后都不是白白的区间了,则称这是一个极长的白白的区间。有 $q$ 次操作,每次操作会修改某个位置的值,或者把某个位置变成黑色。每次操作后,求所有极长的白白的区间中含有的逆序对数的异或和。强制在线。

【输入格式】

第一行两个正整数 $n, q$。

第二行 $n$ 个正整数 $a_1, a_2, \dots, a_n$。

接下来 $q$ 行,每行表示一次操作,每行的第一个数表示操作的种类:

$• ~ 0 ~ x ~ y$ 表示把 $a_x$ 改为 $y$

$• ~ 1 ~ x$ 表示把第 $x$ 个位置变成黑色

保证每次操作时的第 $x$ 个位置是白色的。

$x$ 和 $y$ 需要异或上一次输出的答案(若是第一次操作则无需异或)。

【输出格式】

$q$ 行,每行一个整数,表示每次询问的答案。

【样例输入】

4 3
6 0 10 1
1 2
1 0
1 2

【样例输出】

1
1
0

【数据范围与提示】

$n ≤ 150000,q ≤ 20000,0 ≤ a_i ≤ 10^9,1 ≤ x ≤ n,0 ≤ y ≤ 10^9$

$Subtask1(10pts) : n ≤ 10^3, q ≤ 10^3$
$Subtask2(20pts) : 只有 0 操作$
$Subtask3(30pts) : 只有 1 操作$
$Subtask4(40pts) : 没有特殊限制$

题解

这是一道极其毒瘤的数据结构题

求一段区间的逆序对个数,自然是用树状数组,但要支持修改和分裂,那么就套主席树,启发式分裂

效率:$ O(nlog^3n) $,很遗憾,这样子常数太大,而且这道题卡常

出题人发现,其实修改和分裂是可以分开做的

修改时直接在树套树上查询和修改即可,至于分裂时,其实要找的只有在它前面比它大的个数,可以每一个区间开一个 splay 查询,类似于启发式,暴力删,然后重构一个新的 splay

其实就是利用分裂时的特殊性质用 splay 优化树套树的分裂过程

大概要分讨一下前面的区间和后面的区间的大小

时间效率:$ O(nlog^2n)$

写到醉生梦死

代码

 #include<bits/stdc++.h>
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
int R(){
int x;bool f=;char ch;_(!)if(ch=='-')f=;x=ch^;
_()x=(x<<)+(x<<)+(ch^);return f?x:-x;}
const int N=2e5+,INF=1e9+;
int n,q,a[N],Rt[N],li[N];
LL ans,las,sum[N];
set<int>s;
set<int>::iterator it;
class Splay{
private:
#define Ls(x) ch[x][0]
#define Rs(x) ch[x][1]
public:
int val[N*],fa[N*],siz[N*],num[N*],ch[N*][],cnt;
void pushup(int x){siz[x]=siz[Ls(x)]+siz[Rs(x)]+num[x];}
int init(){
cnt+=;
fa[cnt]=cnt-,Rs(cnt-)=cnt;
val[cnt]=INF,val[cnt-]=;
siz[cnt]=siz[cnt-]=num[cnt]=num[cnt-]=;
return cnt-;
}
int find_xi(int rt,int x){
if(!rt)return ;
if(val[rt]==x)return rt;
if(val[rt]<x){
int f=find_xi(Rs(rt),x);
return f?f:rt;
}
return find_xi(Ls(rt),x);
}
int find_da(int rt,int x){
if(!rt)return ;
if(val[rt]==x)return rt;
if(val[rt]>x){
int f=find_da(Ls(rt),x);
return f?f:rt;
}
return find_da(Rs(rt),x);
}
void rotate(int &k,int x){
int y=fa[x],z=fa[y],fl=(Rs(y)==x),w=ch[x][!fl];
if(y==k)k=x;
else ch[z][Rs(z)==y]=x;
ch[x][!fl]=y,ch[y][fl]=w;
if(w)fa[w]=y;fa[y]=x,fa[x]=z;
pushup(y),pushup(x);
return;
}
void splay(int &k,int x){
while(x!=k){
int y=fa[x];
if(y!=k)
rotate(k,(Ls(fa[x])==x)^(Ls(fa[y])==y)?x:y);
rotate(k,x);
}
}
void insert(int id,int x,int v){
int now=find_xi(Rt[id],x);
if(val[now]==x){
splay(Rt[id],now),num[now]+=v,pushup(now);
return;
}
int nex=find_da(Rt[id],x);
splay(Rt[id],now),splay(Rs(now),nex);
cnt++,val[cnt]=x,num[cnt]=siz[cnt]=;
fa[cnt]=nex,ch[nex][]=cnt;
pushup(nex),pushup(now);
return;
}
#undef Ls
#undef Rs
}T;
int cnt,rot[N];
struct seg{int ls,rs,v;}tr[N*];
#define Ls tr[rt].ls
#define Rs tr[rt].rs
LL query(int rt,int l,int r,int ql,int qr){
if(!rt)return ;
if(ql<=l&&qr>=r)return tr[rt].v;
int mid=(l+r)>>;LL res=;
if(ql<=mid)res=query(Ls,l,mid,ql,qr);
if(qr>mid)res+=query(Rs,mid+,r,ql,qr);
return res;
}
void insert(int &rt,int l,int r,int k,int v){
if(!rt)rt=++cnt;tr[rt].v+=v;
if(l==r)return;
int mid=(l+r)>>;
if(k<=mid)insert(Ls,l,mid,k,v);
else insert(Rs,mid+,r,k,v);
return;
}
LL ask(int l,int r,int ql,int qr){
if(ql>qr||l>r)return ;
LL ans=;
for(;r;r-=r&-r)ans+=query(rot[r],,INF,ql,qr);
for(;l;l-=l&-l)ans-=query(rot[l],,INF,ql,qr);
return ans;
}
void add(int k,int x,int f){for(;k<=n;k+=k&-k)insert(rot[k],,INF,x,f);}
int main(){
n=R(),q=R(),Rt[n]=T.init();
for(int i=;i<=n;i++)
a[i]=R()+,T.insert(n,a[i],);
s.insert(n),li[n]=;
for(int i=;i<=n;i++){
ans+=ask(,i-,a[i]+,INF);
add(i,a[i],);}
sum[n]=ans;
while(q--){
int op=R(),x=(LL)R()^las,y,l,r;
it=s.lower_bound(x);
r=(*it),l=li[r],ans^=sum[r];
if(!op){
y=(LL)(R()^las)+;
sum[r]-=ask(l-,x-,a[x]+,INF)+ask(x,r,,a[x]-);
T.insert(r,a[x],-),T.insert(r,y,);
add(x,a[x],-),add(x,a[x]=y,);
sum[r]+=ask(l-,x-,a[x]+,INF)+ask(x,r,,a[x]-);
ans^=sum[r];
}
if(op){
li[r]=x+,T.insert(r,a[x],-);
s.insert(x-),li[x-]=l,sum[x-]=;
LL num=(LL)ask(l-,x-,a[x]+,INF)+ask(x,r,,a[x]-);
if(x-l<r-x){
if(l!=x)Rt[x-]=T.init();
for(int i=l;i<x;i++){
T.insert(r,a[i],-);
int k=T.find_da(Rt[r],a[i]);
T.splay(Rt[r],k),num+=T.siz[T.ch[k][]];
T.insert(x-,a[i],);
k=T.find_da(Rt[x-],a[i]);
T.splay(Rt[x-],k);
sum[x-]+=T.siz[T.ch[k][]];
}
sum[r]-=num,ans^=sum[x-]^sum[r];
}
else{
swap(sum[r],sum[x-]),swap(Rt[r],Rt[x-]);
Rt[r]=T.init(),sum[r]=;
for(int i=r;i>x;i--){
T.insert(x-,a[i],-);
int k=T.find_da(Rt[x-],a[i]);
T.splay(Rt[x-],k),num+=T.siz[T.ch[k][]];
T.insert(r,a[i],);
k=T.find_da(Rt[r],a[i]);
T.splay(Rt[r],k);
sum[r]+=T.siz[T.ch[k][]];
}
sum[x-]-=num,ans^=sum[x-]^sum[r];
}
}
printf("%lld\n",las=ans);
}
return ;
}

白白的(baibaide)——树状数组套主席树+splay的更多相关文章

  1. BZOJ 3196 Tyvj 1730 二逼平衡树 ——树状数组套主席树

    [题目分析] 听说是树套树.(雾) 怒写树状数组套主席树,然后就Rank1了.23333 单点修改,区间查询+k大数查询=树状数组套主席树. [代码] #include <cstdio> ...

  2. BZOJ 1901 Zju2112 Dynamic Rankings ——树状数组套主席树

    [题目分析] BZOJ这个题目抄的挺霸气. 主席树是第一时间想到的,但是修改又很麻烦. 看了别人的题解,原来还是可以用均摊的思想,用树状数组套主席树. 学到了新的姿势,2333o(* ̄▽ ̄*)ブ [代 ...

  3. BZOJ_3196_Tyvj 1730 二逼平衡树_树状数组套主席树

    BZOJ_3196_Tyvj 1730 二逼平衡树_树状数组套主席树 Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排 ...

  4. ZOJ 2112 Dynamic Rankings(树状数组套主席树 可修改区间第k小)题解

    题意:求区间第k小,节点可修改 思路:如果直接用静态第k小去做,显然我更改一个节点后,后面的树都要改,这个复杂度太高.那么我们想到树状数组思路,树状数组是求前缀和,那么我们可以用树状数组套主席树,求出 ...

  5. P2617 Dynamic Rankings(树状数组套主席树)

    P2617 Dynamic Rankings 单点修改,区间查询第k大 当然是无脑树套树了~ 树状数组套主席树就好辣 #include<iostream> #include<cstd ...

  6. [COGS257]动态排名系统 树状数组套主席树

    257. 动态排名系统 时间限制:5 s   内存限制:512 MB [问题描述]给定一个长度为N的已知序列A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作:1.查询A[ ...

  7. BZOJ 2141 排队(树状数组套主席树)

    解法很多的题,可以块套树状数组,可以线段树套平衡树.我用的是树状数组套主席树. 题意:给出一段数列,m次操作,每次操作是交换两个位置的数,求每次操作后的逆序对数.(n,m<=2e4). 对于没有 ...

  8. 洛谷P3759 [TJOI2017]不勤劳的图书管理员 【树状数组套主席树】

    题目链接 洛谷P3759 题解 树状数组套主席树板题 #include<algorithm> #include<iostream> #include<cstring> ...

  9. Codeforces Round #404 (Div. 2) E. Anton and Permutation(树状数组套主席树 求出指定数的排名)

    E. Anton and Permutation time limit per test 4 seconds memory limit per test 512 megabytes input sta ...

  10. 【Luogu】P2617Dynamic Ranking(树状数组套主席树)

    题目链接 树状数组套主席树有点难懂qwq 不好理解 树状数组套主席树的直观理解应该是:树状数组的每一个节点是一棵主席树. 普通区间修改我们是创建1个线段树,树状数组套主席树的时候我们就创建log个线段 ...

随机推荐

  1. jsoup抓取网页内容

    java项目有时候我们需要别人网页上的数据,怎么办?我们可以借助第三方架包jsou来实现,jsoup的中文文档,那怎么具体的实现呢?那就跟我一步一步来吧 最先肯定是要准备好这个第三方架包啦,下载地址, ...

  2. C#winform拖拽实现获得文件路径

    1.关键知识点说明: 通过DragEnter事件获得被拖入窗口的“信息”(可以是若干文件,一些文字等等),在DragDrop事件中对“信息”进行解析.窗体的AllowDrop属性必须设置成true;且 ...

  3. django-pagination分页

    1. 将该APP安装至Django项目中.(settings.py) INSTALLED_APPS = ( # ... 'pagination', ) 2. 在Django项目的middleware中 ...

  4. STL stl_construct.h

    stl_construct.h // Filename: stl_construct.h // Comment By: 凝霜 // E-mail: mdl2009@vip.qq.com // Blog ...

  5. px-rem自适应转换(进阶@rem:40rem; )

    接力之前的文章 https://www.cnblogs.com/leshao/p/5674710.html 这篇文章讲解的是px -rem 单位换算 除100的  写法 比如实际测量PSD宽度是500 ...

  6. WPF中Xaml编译正常而Designer Time时出错的解决办法

    开发wpf时我们经常遇到一个xaml文件在设计时显示解析错误(比如在:VS或者Blend)而编译正常运行正常. 原因是:xaml的在Debug版本下必须为anyCPU. 解决办法: 1.打开工程文件x ...

  7. poj 2388 Who's in the Middle(快速排序求中位数)

    一.Description FJ is surveying his herd to find the most average cow. He wants to know how much milk ...

  8. spring初始化顺序

    首先,Spring bean的默认加载顺序是怎么控制的 工程中有2个bean,A和B,其中必须先初始化A再初始化B,但是没有depend-on或者Order等方式去保证,只不过恰好刚好这么运行着没出事 ...

  9. oracle--分页过程demo1

    oracle分页过程demo1: --ROWNUM用法 select o.*,rownum rn from (select * from emp) o where rownum<=10; sel ...

  10. Linux 文件名颜色

    在Linux中,文件的颜色都是有含义的.其中, 蓝色表示目录 绿色表示可执行文件 红色表示压缩文件 浅蓝色表示链接文件 灰色表示其它文件 红色闪烁表示链接的文件有问题了 黄色是设备文件,包括block ...