zoukankan      html  css  js  c++  java
  • [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
    ",ans);
        return 0;
    }
    /*
     * 求最少交换次数,注意到每个点的交换带来的影响是独立的,因此只考虑每个点
     * 是否交换。于是问题转化为求横跨两个兄弟结点子树中元素的逆序对数。
     * 把左树的元素添加进一个权值线段树,另一个树的元素在这棵树上做查询即可
     * 然后将另一个树的元素插入这个线段树,然后向上回溯即可
     * 事实上我们必然会建立一个右树的线段树,因此直接线段树合并即可
     * 你发现在合并的时侯就可以统计逆序对数了,于是查询的过程也省了
     * 即线段树合并
     * BUG#1: 没开LL
     * BUG#2: min函数忘开LL,下次还是用STD好
     */
    
    
  • 相关阅读:
    Web性能优化系列(3):如何延迟加载JS
    Web性能优化系列(2):剖析页面绘制时间
    Web性能优化系列(1):Web性能优化分析
    页面制作之开发调试工具(1)
    格式化 SQL 来提高效率
    关于SQL注入,你应该知道的那些事
    jQuery()方法的第二个参数
    JSON简介以及用法代码汇总
    js简单的面试题
    常用meta整理
  • 原文地址:https://www.cnblogs.com/sshwy/p/10989318.html
Copyright © 2011-2022 走看看