您的位置:首页 > 其它

[BZOJ2212]Tree Rotation

2017-12-19 18:43 85 查看
之前比赛时有过这题,当时不理解,现在重温一下

用权值线段树维护每一个节点的子树有哪些数值,当合并两棵子树时,线段树合并的过程中统计答案

设左儿子的线段树当前合并到节点$x$,右儿子的线段树当前合并到节点$y$

如果不交换,那么逆序对数量为$siz_{x.r}\cdot siz_{y.l}$,如果交换,逆序对数量为$siz_{x.l}\cdot siz_{y.r}$

所以线段树合并的过程中我们可以找到交换或不交换左右儿子的(两个儿子都有贡献的答案),记为$cnt_1,cnt_2$

所以每次合并完之后答案$+=min\{cnt_1,cnt_2\}$

这个过程是可以边读入边搞的,所以不用实际建出二叉树

#include<stdio.h>
#define ll long long
int siz[4000010],ls[4000010],rs[4000010],tot,n;
void insert(int p,int l,int r,int&x){
if(x==0)x=++tot;
siz[x]=1;
if(l==r)return;
int mid=(l+r)>>1;
if(p<=mid)
insert(p,l,mid,ls[x]);
else
insert(p,mid+1,r,rs[x]);
}
ll r1,r2,ans;
int merge(int a,int b){
if(a==0)return b;
if(b==0)return a;
r1+=siz[rs[a]]*(ll)siz[ls[b]];
r2+=siz[ls[a]]*(ll)siz[rs[b]];
ls[a]=merge(ls[a],ls[b]);
rs[a]=merge(rs[a],rs[b]);
siz[a]=siz[ls[a]]+siz[rs[a]];
return a;
}
ll min(ll a,ll b){return a<b?a:b;}
int dfs(){
int v,s;
scanf("%d",&v);
if(v==0){
int l=dfs();
int r=dfs();
r1=r2=0;
s=merge(l,r);
ans+=min(r1,r2);
return s;
}else{
s=++tot;
insert(v,1,n,s);
return s;
}
}
int main(){
scanf("%d",&n);
dfs();
printf("%lld",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: