1 条题解
-
0
自动搬运
来自洛谷,原作者为

541forever
exlg伪犇验证搬运于
2025-08-24 21:59:48,当前版本为作者最后更新于2022-04-05 10:39:16,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这是一道比较裸的平衡树模板题,这是一篇
可能对细(keng)节(dian)解释比较详尽的屑题解。思路与 文艺平衡树 相似,我们需要先增加两个哨兵节点,然后每次执行区间翻转操作时,就对区间左端点的前驱和右端点的后继进行 Splay 并把不断交换他们子节点的左右子树。而对于求解答案,我们要求的是这个节点的排名(即它在当前序列中的位置),我们可以将该节点提到根,然后统计它的左子树的大小 size(这个 size 没有算上这个节点本身,但算上了哨兵节点,所有两个影响抵消)。具体实现时,我们可以记录每个权值在平衡树中对应的节点编号记为
pos[i],然后每次翻转时找到当前节点pos[i]的后继,而在我们进行这一步操作前,将要操作的值前面的数已经是有序的了,所有它的前驱便是pos[i-1]。注意:
- 我们不太能直接以编号作为平衡树的权值进行操作,这样虽然仍能实现区间翻转,但查一个数在序列中的位置会较为麻烦。
- 区间翻转需要提到根上的点时左端点的前驱和右端点的后继,而不是左端点和右端点(本蒟蒻就是因为这里一直不知道哪错了)。
- 给定数组的值可能会有相等而我们需要按输入的原始次序操作,所以我们可以将原序列重新按离散的方式重新赋值。
下面是丑陋的代码:
#include<bits/stdc++.h> #define int long long using namespace std; int n,val[300005],tag[300005],ch[300005][2],tot,rt,fa[300005],si[300005],p[300005];//p记录权值在平衡树中对应的点的编号,tag作为懒标记记录是否需要交换左右子树 struct object{ int val,num; }ob[300005]; inline int read(){ int x=0,f=1; char ch; do{ ch=getchar(); if(ch=='-'){ f=-1; } }while(!(ch>='0'&&ch<='9')); while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+ch-'0'; ch=getchar(); } return x*f; } struct Splay{ void push_up(int cur){ si[cur]=si[ch[cur][1]]+si[ch[cur][0]]+1; } int get(int x){ return x==ch[fa[x]][1]; } int build(int l,int r,int f){ if(l>r){ return 0; } int mid=(l+r)>>1; int now=++tot; fa[now]=f; val[now]=ob[mid].val; p[ob[mid].val]=now; ch[now][0]=build(l,mid-1,now); ch[now][1]=build(mid+1,r,now); push_up(now); return now; } void push_down(int cur){ if(tag[cur]){ tag[cur]=0; swap(ch[cur][0],ch[cur][1]); tag[ch[cur][0]]^=1; tag[ch[cur][1]]^=1; } } int nex(){//找到后继 push_down(rt); int x=ch[rt][1]; while(1){ push_down(x); if(!ch[x][0]){ break; } x=ch[x][0]; } return x; } void rotate(int x){ int y=fa[x]; int z=fa[y]; push_down(y); push_down(x); int chk=get(x); ch[y][chk]=ch[x][chk^1]; if(ch[x][chk^1]){ fa[ch[x][chk^1]]=y; } fa[y]=x; ch[x][chk^1]=y; fa[x]=z; if(z){ ch[z][y==ch[z][1]]=x; } push_up(y); push_up(x); } void splay(int x,int y){ for(int f;(f=fa[x])!=y;rotate(x)){ if(fa[f]==y){ continue; } if(get(x)==get(f)){ rotate(f); }else{ rotate(x); } } if(y==0){ rt=x; } } void reverse(int l,int r){ splay(l,0); splay(r,l); tag[ch[r][0]]^=1; } }tree;//Splay模板 bool cmp(object a,object b){ if(a.val<b.val){ return 1; }else{ if(a.val==b.val){ return a.num<b.num; } } return 0; } bool cmp2(object a,object b){ return a.num<b.num; } signed main(){ n=read(); for(int i=1;i<=n;++i){ ob[i+1].val=read(); ob[i+1].num=i+1; } ob[1].val=0; ob[n+2].val=n+1; sort(ob+2,ob+n+2,cmp); for(int i=2;i<=n+1;i++){ //离散 ob[i].val=i-1; } sort(ob+2,ob+2+n,cmp2); rt=tree.build(1,n+2,0);//以类似线段树的方式建数 for(int i=1;i<=n;++i){ int x=p[i];//找到该权值对应的点的位置 tree.splay(x,0); printf("%lld ",si[ch[x][0]]); x=tree.nex();//该权值的后继 int y=p[i-1];//该权值的前驱 tree.reverse(y,x);//区间翻转 } return 0; }
- 1
信息
- ID
- 3363
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者