[POI2011]ROT-Tree Rotations
Posted sshwy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[POI2011]ROT-Tree Rotations相关的知识,希望对你有一定的参考价值。
题意:给一棵n(1≤n≤200000个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少。
算法见注释
#include<cstdio>
#define ll long long
using namespace std;
const int N=5e6+6;
int n,tot;
int lc[N],rc[N];
ll sz[N];
ll c1,c2,ans;
ll min(ll a,ll b)return a<b?a:b;
int create(int v,int l=1,int r=n)
int u=++tot;
//build a ST for a single value, return u
if(l==r)return sz[u]++, u;
int mid=(l+r)>>1;
if(v<=mid)lc[u]=create(v,l,mid);
else rc[u]=create(v,mid+1,r);
return sz[u]=sz[lc[u]]+sz[rc[u]], u;
int merge(int x,int y)//merge two ST, return new root
if(!x||!y)return x+y;
c1+=sz[rc[x]]*sz[lc[y]];
c2+=sz[lc[x]]*sz[rc[y]];
lc[x]=merge(lc[x],lc[y]), rc[x]=merge(rc[x],rc[y]);
sz[x]=sz[lc[x]]+sz[rc[x]];
return x;//y add to x
int calc()//calc for subtree u, return the node of ST
int a;
scanf("%d",&a);
if(a)return create(a);//a single node dont has c1bution.
int x=calc(), y=calc();
c1=0,c2=0;
int z=merge(x,y);
ans+=min(c1,c2);
return z;
int main()
scanf("%d",&n);
calc();
printf("%lld\n",ans);
return 0;
/*
* 求最少交换次数,注意到每个点的交换带来的影响是独立的,因此只考虑每个点
* 是否交换。于是问题转化为求横跨两个兄弟结点子树中元素的逆序对数。
* 把左树的元素添加进一个权值线段树,另一个树的元素在这棵树上做查询即可
* 然后将另一个树的元素插入这个线段树,然后向上回溯即可
* 事实上我们必然会建立一个右树的线段树,因此直接线段树合并即可
* 你发现在合并的时侯就可以统计逆序对数了,于是查询的过程也省了
* 即线段树合并
* BUG#1: 没开LL
* BUG#2: min函数忘开LL,下次还是用STD好
*/
以上是关于[POI2011]ROT-Tree Rotations的主要内容,如果未能解决你的问题,请参考以下文章
LuoguP3521 [POI2011]ROT-Tree Rotations
P3521 [POI2011]ROT-Tree Rotations
线段树合并(POI2011ROT-Tree Rotations)
[POI2011]ROT-Tree Rotations 线段树合并|主席树 / 逆序对