zoukankan      html  css  js  c++  java
  • [POI2011]ROT-Tree Rotations 线段树合并|主席树 / 逆序对

    题目[POI2011]ROT-Tree Rotations

    【Description】

    现在有一棵二叉树,所有非叶子节点都有两个孩子.在每个叶子节点上有一个权值(有(n)个叶子节点,满足这些权值为(1..n)的一个排列).可以任意交换每个非叶子节点的左右孩子.

    要求进行一系列交换,使得最终所有叶子节点的权值按照中序遍历写出来,逆序对个数最少.

    【Input Format】

    第一行一个整数(n).

    下面每行,一个数(x).

    如果(x =0),表示这个节点非叶子节点,递归地向下读入其左孩子和右孩子的信息.

    如果(x e 0),表示这个节点是叶子节点,权值为(x).

    【Output Format】

    一行,最少逆序对个数.

    【Sample Input】

    3
    0
    0
    3
    1
    2
    

    【Sample Output】

    1
    

    【Hint】

    对于$ 30%$的数据:(2<=n<=5000)

    对于(100\%)的数据:(2<=n<=200000)


    思路

    首先需要明确的是,我们可以分层对逆序对进行计算.对于一个非叶子节点,我们可以先只计算它左子树与右子树中的叶结点形成的逆序对,至于它的某一个子树中的叶结点形成的逆序对,我们可以在下一步递归计算.

    其次,在上述"分层计算"的思路中,当前层节点左右子树的交换,对向上一层的逆序对计数没有影响.换句话说,对一个节点(p),无论(LC[p])(或(RC[p]))的两个子树交不交换,在(LC[p])(RC[p])这一层计算出的逆序对数都是一样的.

    到这里,很容易设计出算法:对于每个非叶子节点,计算交换与不交换它的子树这两种情况所产生的逆序对数,取最优值累加答案就可以了.可以考虑通过线段树合并的方式,把子树里叶结点的权值维护在权值线段树中,自底向上合并,利用线段树来求逆序对.

    接下来的问题就是如何利用线段树里的数据求出逆序对.考试时我采用的方法是用类似DFS序的序列记录某一棵子树中具体包括了哪些权值,再用(O(nlogn))权值线段树求逆序对的基本算法求解.实际上,可以直接在将两个(线段树上的)节点合并为一个的时候,累加计算一个(线段树上的)节点小于等于(mid)(线段树区间中点)的值的个数另一个(线段树上的)节点)大于等于(mid)的值的个数的乘积,由于在线段树上,值域是被覆盖完全的,所以累加的答案就是(题目中的树)这一层产生的逆序对数.

    代码实现比较短,却涉及到对算法的本质的理解.


    代码

    #include<algorithm>
    #include<cstdio>
    using namespace std;
    #define SIZE 400005
    #define SEGSIZE 4000005
    int n,Root[SIZE],L[SIZE],R[SIZE],weight[SIZE],Totx;
    long long P1,P2,ans; 
    //动态开点权值线段树 
    struct SegTree
    {
    	#define Mid ((X+Y)>>1)
    	int sum[SEGSIZE],LC[SEGSIZE],RC[SEGSIZE],P;
    	void change(int &x,int pos,int X,int Y)
    	{
    		if(x==0)x=++P;
    		sum[x]++;
    		if(X==Y)return;
    		if(Mid>=pos)change(LC[x],pos,X,Mid);
    		else change(RC[x],pos,Mid+1,Y);
    	}
    	int Merge(int u,int v,int X,int Y)
    	{
    		if(u==0||v==0)return u?u:v;
    		P1+=(long long)sum[RC[u]]*sum[LC[v]];
    		P2+=(long long)sum[LC[u]]*sum[RC[v]];
    		sum[u]=sum[u]+sum[v];
    		LC[u]=Merge(LC[u],LC[v],X,Mid);
    		RC[u]=Merge(RC[u],RC[v],Mid+1,Y);
    		return u;
    	}
    }T;
    //递归建图
    int Read()
    {
    	int x,u=++Totx;
    	scanf("%d",&x);
    	if(x>0)weight[u]=x;
    	else{ L[u]=Read(); R[u]=Read(); }
    	return u;
    }
    //深搜 自底向上合并 
    void DFS(int u)
    {
    	if(weight[u])T.change(Root[u],weight[u],1,n);
    	else
    	{
    		DFS(L[u]);
    		DFS(R[u]);
    		P1=0,P2=0;
    		Root[u]=T.Merge(Root[u],Root[L[u]],1,n);
    		Root[u]=T.Merge(Root[u],Root[R[u]],1,n);
    		ans+=min(P1,P2);
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	Read();
    	DFS(1);
    	printf("%lld
    ",ans);
    	return 0;
    } 
    
    

    附录

    考场代码((TLE))

    #include<algorithm>
    #include<cstdio>
    using namespace std;
    #define SIZE 400005
    #define SEGSIZE 8000005
    int n,Root[SIZE],weight[SIZE],L[SIZE],R[SIZE],ans;
    
    //动态开点权值线段树 
    struct SegTree
    {
    	#define Mid ((X+Y)>>1)
    	int sum[SEGSIZE],LC[SEGSIZE],RC[SEGSIZE],P;
    	int query(int x,int L,int R,int X,int Y)
    	{
    		if(x==0||Y<L||X>R)return 0;
    		if(X>=L&&Y<=R)return sum[x];
    		return query(LC[x],L,R,X,Mid)+query(RC[x],L,R,Mid+1,Y);
    	}
    	void change(int &x,int pos,int X,int Y,int v)
    	{
    		if(x==0)x=++P;
    		sum[x]+=v;
    		if(X==Y)return;
    		if(Mid>=pos)change(LC[x],pos,X,Mid,v);
    		else change(RC[x],pos,Mid+1,Y,v);
    	}
    	int Merge(int u,int v,int X,int Y)
    	{
    		if(u==0||v==0)return u?u:v;
    		int x=++P;
    		sum[x]=sum[u]+sum[v];
    		LC[x]=Merge(LC[u],LC[v],X,Mid);
    		RC[x]=Merge(RC[u],RC[v],Mid+1,Y);
    		return x;
    	}
    }T;
    
    //递归建图 求DFS序 
    int Totx=0,DFx[SIZE],DFx_C,Lx[SIZE],Rx[SIZE];
    int Read()
    {
    	int x,u=++Totx;
    	scanf("%d",&x);
    	Lx[u]=DFx_C+1;
    	if(x>0){ weight[u]=x; DFx[++DFx_C]=x; }
    	if(x==0){ L[u]=Read(); R[u]=Read(); }
    	Rx[u]=DFx_C;
    	return u;
    }
     
    //深搜 自底向上合并 
    void DFS(int u)
    {
    	if(weight[u])T.change(Root[u],weight[u],1,n,1);
    	else
    	{
    		DFS(L[u]);
    		DFS(R[u]);
    		
    		int P1=0,P2=0;
    		for(int i=Lx[L[u]];i<=Rx[L[u]];i++)
    			P1+=T.query(Root[R[u]],1,DFx[i],1,n);//O(nlogn)计算逆序对
    		for(int i=Lx[R[u]];i<=Rx[R[u]];i++)
    			P2+=T.query(Root[L[u]],1,DFx[i],1,n);		
    		ans+=min(P1,P2);
    		
    		Root[u]=T.Merge(Root[u],Root[L[u]],1,n);
    		Root[u]=T.Merge(Root[u],Root[R[u]],1,n);
    	}
    }
    
    int main()
    {
    	freopen("tree.in","r",stdin);
    	freopen("tree.out","w",stdout);
    	scanf("%d",&n);
    	Read();
    	DFS(1);
    	printf("%d
    ",ans);
    	return 0;
    } 
    
  • 相关阅读:
    Android通过包名启动应用程序
    android 通过反射方法获取状态栏高度
    Android在输入法界面监听按键(以返回键为例)
    Android主动弹出输入法
    Android使用内部类自定义控件
    Android中ListView列表Item的圆角效果实现
    Android中关于系统Dialog无法全屏的问题
    Dart学习记录(五)
    Dart学习记录(四)—— 库
    Dart学习记录(三)—— 泛型
  • 原文地址:https://www.cnblogs.com/TaylorSwift13/p/11178388.html
Copyright © 2011-2022 走看看