[BZOJ 2212] [Poi2011] Tree Rotations 【线段树合并】

题目链接:BZOJ - 2212

题目分析

子树 x 内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆序对。

左右子树内部的逆序对与是否交换左右子树无关,是否交换左右子树取决于交换后 “跨越 x 左子树与右子树的逆序对” 是否会减小。

因此我们要求出两种情况下的逆序对数,使用线段树合并,对每个节点建一棵线段树,然后合并的同时就求出两种情况下的逆序对。

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

inline void Read(int &Num)
{
	char c = getchar();
	while (c < ‘0‘ || c > ‘9‘) c = getchar();
	Num = c - ‘0‘; c = getchar();
	while (c >= ‘0‘ && c <= ‘9‘)
	{
		Num = Num * 10 + c - ‘0‘;
		c = getchar();
	}
}

typedef long long LL;

inline LL gmin(LL a, LL b) {return a < b ? a : b;}

const int MaxN = 400000 + 5, MaxNode = 4000000 + 5;

int n, IndexT, Index, RT;
int A[MaxN], Tree[MaxN][2], Root[MaxN], T[MaxNode], Son[MaxNode][2];

LL Ans0, Ans1, Ans;

void Read_Tree(int &x)
{
	x = ++IndexT;
	Read(A[x]);
	if (A[x] != 0) return;
	Read_Tree(Tree[x][0]);
	Read_Tree(Tree[x][1]);
}

inline void Update(int x)
{
	T[x] = T[Son[x][0]] + T[Son[x][1]];
}

void Insert(int &x, int s, int t, int Pos)
{
	if (x == 0) x = ++Index;
	if (s == t)
	{
		T[x] = 1;
		return;
	}
	int m = (s + t) >> 1;
	if (Pos <= m) Insert(Son[x][0], s, m, Pos);
	else Insert(Son[x][1], m + 1, t, Pos);
	Update(x);
}

int Merge(int x, int y)
{
	if (!x) return y;
	if (!y) return x;
	Ans0 += (LL)T[Son[x][1]] * (LL)T[Son[y][0]];
	Ans1 += (LL)T[Son[x][0]] * (LL)T[Son[y][1]];
	Son[x][0] = Merge(Son[x][0], Son[y][0]);
	Son[x][1] = Merge(Son[x][1], Son[y][1]);
	Update(x);
	return x;
}

void Solve(int x)
{
	if (A[x]) return;
	Solve(Tree[x][0]); Solve(Tree[x][1]);
	Ans0 = Ans1 = 0;
	Root[x] = Merge(Root[Tree[x][0]], Root[Tree[x][1]]);
	Ans += gmin(Ans0, Ans1);
}

int main()
{
	scanf("%d", &n);
	Read_Tree(RT);
	for (int i = 1; i <= IndexT; ++i)
		if (A[i] != 0) Insert(Root[i], 1, n, A[i]);
	Solve(RT);
	cout << Ans << endl;
	return 0;
}

  

时间: 2024-11-05 07:00:08

[BZOJ 2212] [Poi2011] Tree Rotations 【线段树合并】的相关文章

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

原文链接http://www.cnblogs.com/zhouzhendong/p/8079786.html 题目传送门 - BZOJ3286 题意概括 给一棵n(1≤n≤200000个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少. 题解 线段树合并. 博主很懒,题解不写了. 这份代码是仿照别人的写的. 代码 #include <cstring> #include <cstdio> #include <cmath> #include <al

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

bzoj 2212 : [Poi2011]Tree Rotations (线段树合并)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2212 思路:用线段树合并求出交换左右儿子之前之后逆序对的数量,如果数量变小则交换. 实现代码: #include<bits/stdc++.h> using namespace std; #define ll long long const int M = 4e5+10; int n,cnt,idx; ll ans,cnt1,cnt2; int v[M],l[M],r[M],root[

[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

bzoj 2212 [Poi2011]Tree Rotations

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 ends with ei

BZOJ 2212 [Poi2011]Tree Rotations(线段树合并)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2212 [题目大意] 给出一棵二叉树,每个叶节点上有一个权值,现在可以任意交换左右儿子, 使得逆序对最少,求最少的逆序对数量 [题解] 我们发现对于每个非叶节点来说,其贡献值为左右两个儿子的权值树上, 每个节点想反位置的数量和乘积,比如左儿子的权值树左节点和右儿子权值树的右节点相乘, 那么我们对于每个节点建立一颗权值线段树,仅保留非0链, 递归合并这些权值线段树,同时每次将相反位置数量

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

bzoj 3413: 匹配 后缀自动机+线段树合并

并不是很难啊,把细节想好了再写就很轻松了~ code: #include <bits/stdc++.h> #define N 200003 #define LL long long #define setIO(s) freopen(s".in","r",stdin) ,freopen(s".out","w",stdout) using namespace std; struct SAM { int tot,last