[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 线段树合并|主席树 / 逆序对

P3521 [POI2011]ROT-Tree Rotations

Luogu P3521 [POI2011]ROT-Tree Rotations