[BZOJ 2212] [Poi2011] Tree Rotations 【線段樹合併】

題目連接:BZOJ - 2212php

 

題目分析

子樹 x 內的逆序對個數爲 :x 左子樹內的逆序對個數 + x 右子樹內的逆序對個數 + 跨越 x 左子樹與右子樹的逆序對。ios

左右子樹內部的逆序對與是否交換左右子樹無關,是否交換左右子樹取決於交換後 「跨越 x 左子樹與右子樹的逆序對」 是否會減少。spa

所以咱們要求出兩種狀況下的逆序對數,使用線段樹合併,對每一個節點建一棵線段樹,而後合併的同時就求出兩種狀況下的逆序對。blog

 

代碼

#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;
}
相關文章
相關標籤/搜索