• [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 Services开发(Java篇)
    前端UI框架小汇总
    WebService 在C#.NET中的使用
    求n*m网格内矩形的数目
    Silverlight学习笔记(三):控件布局管理
    Silverlight学习笔记(二):创建Hello World 项目(备注)
    ExcelHelp 学习笔记二: C# 往Excel中写入数据
    DataBase 学习笔记一:c#连接SQL数据库
    ExcelHelp 学习笔记一: C#读取Excel中数据
    Silverlight学习笔记(二):创建Hello World 简单基础应用
  • 原文地址:https://www.cnblogs.com/sshwy/p/10989318.html
Copyright © 2020-2023  润新知