BZOJ2212 [Poi2011]Tree Rotations 线段树合并 逆序对

原文链接http://www.cnblogs.com/zhouzhendong/p/8079786.html


题目传送门 - BZOJ3286


题意概括

  给一棵n(1≤n≤200000个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少。


题解

  线段树合并。

  博主很懒,题解不写了。

  这份代码是仿照别人的写的。


代码

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int N=200005;
int n,val[N*2],son[N*2][2],tot=0,rt;
int root[N*2],ls[N*2*20],rs[N*2*20];
LL sum[N*2*20];
void dfsread(int &x){
	x=++tot;
	scanf("%d",&val[x]);
	if (!val[x]){
		dfsread(son[x][0]);
		dfsread(son[x][1]);
	}
}
void pushup(int rt){
	sum[rt]=sum[ls[rt]]+sum[rs[rt]];
}
LL sumL,sumR;
void add(int &rt,int L,int R,int v){
	rt=++tot;
	int mid=(L+R)>>1;
	if (L==R){
		sum[rt]=1;
		return;
	}
	if (v<=mid)
		add(ls[rt],L,mid,v);
	else
		add(rs[rt],mid+1,R,v);
	pushup(rt);
}
int merge(int a,int b){
	if (!a||!b)
		return a+b;
	sumL+=sum[rs[a]]*sum[ls[b]];
	sumR+=sum[ls[a]]*sum[rs[b]];
	ls[a]=merge(ls[a],ls[b]);
	rs[a]=merge(rs[a],rs[b]);
	pushup(a);
	return a;
}
LL dfs(int x){
	LL ans=0;
	if (!val[x]){
		ans=dfs(son[x][0])+dfs(son[x][1]);
		sumL=sumR=0;
		root[x]=merge(root[son[x][0]],root[son[x][1]]);
		ans+=min(sumL,sumR);
	}
	else
		add(root[x],1,n,val[x]);
	return ans;
}
int main(){
	scanf("%d",&n);
	dfsread(rt);
	printf("%lld",dfs(rt));
	return 0;
}

  

时间: 2024-10-16 12:27:06

BZOJ2212 [Poi2011]Tree Rotations 线段树合并 逆序对的相关文章

bzoj2212 [Poi2011]Tree Rotations 线段树合并

Description Byteasar the gardener is growing a rare tree called Rotatus Informatikus. It has some interesting features: The tree consists of straight branches, bifurcations and leaves. The trunk stemming from the ground is also a branch. Each branch

[POI2011]ROT-Tree Rotations 线段树合并|主席树 / 逆序对

题目[POI2011]ROT-Tree Rotations [Description] 现在有一棵二叉树,所有非叶子节点都有两个孩子.在每个叶子节点上有一个权值(有\(n\)个叶子节点,满足这些权值为\(1..n\)的一个排列).可以任意交换每个非叶子节点的左右孩子. 要求进行一系列交换,使得最终所有叶子节点的权值按照中序遍历写出来,逆序对个数最少. [Input Format] 第一行一个整数\(n\). 下面每行,一个数\(x\). 如果\(x =0\),表示这个节点非叶子节点,递归地向下读

BZOJ_2212_[Poi2011]Tree Rotations_线段树合并

Description Byteasar the gardener is growing a rare tree called Rotatus Informatikus. It has some interesting features: The tree consists of straight branches, bifurcations and leaves. The trunk stemming from the ground is also a branch. Each branch

bzoj2212 Tree Rotations 线段树合并+动态开点

题目传送门 思路: 区间合并线段树的题,第一次写,对于一颗子树,无论这个子树怎么交换,都不会对其他子树的逆序对造成影响,所以就直接算逆序对就好. 注意叶子节点是1到n的全排列,所以每个权值都只会出现1次,合并很好写. 注意动态开点,最多n个叶子节点,然后每次查询用到log个子树节点,(这句话似乎有语病)所以要开nlogn的空间. #include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) #define fpn() freope

HDU 1394 Minimum Inversion Number(线段树求逆序对)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1394 解题报告:给出一个序列,求出这个序列的逆序数,然后依次将第一个数移动到最后一位,求在这个过程中,逆序数最小的序列的逆序数是多少? 这题有一个好处是输入的序列保证是0 到 n-1,所以不许要离散化,还有一个好处就是在计算在这个序列中比每个数大和小的数一共有多少个的时候可以在O(1)时间计算出来,一开始我没有意识到,还傻傻的用了两层for循环来每次都计算,当然这样果断TLE了.把一个数从第一个移

hdoj 1394 Minimum Inversion Number【线段树求逆序对】

求逆序对有很多算法,这里说一下线段树求逆序对的思想. 知识点:线段树,逆序对,单点更新,成段求和 算法:线段树求逆序数的前提条件是要离散化,变成连续的点,首先建树,每个节点设置一个num值为0. 然后根据逆序对的定义,前面出现过的比当前数大的个数的和,我们需要求前面的比他大的数,其实就相当于从当前a[i]点对他后面所有出现过的数求和一次.然后把当前点的值在线段树叶子节点变为1,表示出现过,并向上更新到线段树里面.比如说样例4 2 1 5 3 首先4后面没有值,4更新为1,4--5区间更新为1,1

HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少是多少? 因为每次相邻的交换操作最多只能减少一个逆序对,所以最多可以减少k个逆序对,所以我们只要求出原来的序列有多少个逆序对然后减去k再跟0取较大的就可以了. 因为数据范围是10的五次方,所以暴力求肯定会TLE,所以要用n*logn算法求逆序对,n*logn算法有几种可以求逆序对的: 线段树,树状数

BNU 2418 Ultra-QuickSort (线段树求逆序对)

题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=2418 解题报告:就是给你n个数,然后让你求这个数列的逆序对是多少?题目中n的范围是n < 500000,所以,暴力是不行的.还是第一次学会用线段树求逆序数,这种方法的时间复杂度是n * log n,是不是很快呢,利用了线段树查询速度快的优势.具体的方法如下: 这里先说一下,如果输入的n个数不是连续的,也就是说把这n个数按从小到大的顺序排列起来不是连续的话,还要先离散化一下,其实也就是把

权值线段树求逆序对问题

我们都知道,求逆序对数量可以用归并排序解决.但是用归并排序只能解决静态的序列问题,没有扩展的区间.因此就有了用权值线段树求逆序对的方法. 1 #include<iostream> 2 #include<iomanip> 3 #include<ctime> 4 #include<climits> 5 #include<algorithm> 6 #include<queue> 7 #include<vector> 8 #inc