码迷,mamicode.com
首页 > 其他好文 > 详细

「Luogu P3521 [POI2011]ROT-Tree Rotations」

时间:2020-04-04 14:38:12      阅读:51      评论:0      收藏:0      [点我收藏+]

标签:lan   space   ddl   fine   次数   维护   原理   左右   权值线段树   

题目大意

给出一颗有 \(n\) 个叶节点的二叉树,且对于所有非叶节点都有两个儿子,每次操作可以交换任意节点的两个儿子,最终要使得先序遍历后叶节点的逆序对最少,输出这个最小值.

分析

考虑交换某个节点的两个儿子对最终答案的影响,交换之后对于子树内的逆序对并不会产生影响,对这颗子树树以外的逆序对也不会产生影响,所以产生的影响只有两颗子树.那么只需要考虑哪棵子树放前面更优就好了.

考虑用权值线段树来维护每个数出现的次数,考虑合并的时候同时计算出两种情况所产生的逆序对个数.

那么考虑如何快速计算左右子树的两种摆放方式的逆序对个数.可以发现在合并的时候会遍历两子树中区间内都非空很多区间,那么可以通过乘法原理来计算逆序对.

代码

#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int MAXN=2e5+5;
const int MAX_NUM=2e5;
const int INF=1e9;
int n;
long long answer=0;
long long minll(long long a,long long b)
{
	if(a<b)
	{
		return a;
	}
	return b;
}
struct SegmentTree
{
	int lson,rson,sum;
}sgt[MAXN*32];
int sgt_cnt=0;
int rubbish[MAXN*32],rubbish_cnt=0;
#define LSON sgt[now].lson
#define RSON sgt[now].rson
#define MIDDLE ((left+right)>>1)
#define LEFT LSON,left,MIDDLE
#define RIGHT RSON,MIDDLE+1,right
int NewPoint()//防止空间不足,写一个空间回收
{
	if(rubbish_cnt)
	{
		return rubbish[rubbish_cnt--];
	}
	return ++sgt_cnt;
}
void DeletePoint(int &now)
{
	sgt[now].lson=sgt[now].rson=0;
	sgt[now].sum=0;
	rubbish[++rubbish_cnt]=now;
	now=0;
}
void PushUp(int now)//合并标记
{
	sgt[now].sum=sgt[LSON].sum+sgt[RSON].sum;
}
void Updata(int place,int &now,int left=1,int right=MAX_NUM)//单点修改
{
	if(place<left||right<place)
	{
		return;
	}
	if(!now)
	{
		now=NewPoint();
	}
	if(left==right)
	{
		sgt[now].sum++;
		return;
	}
	Updata(place,LEFT);
	Updata(place,RIGHT);
	PushUp(now);
}
long long add_1,add_2;
void Merge(int &tree1,int &tree2,int left=1,int right=MAX_NUM,bool first=1)//线段树合并
{
	if(first)
	{
		add_1=add_2=0;
	}
	if(!tree1||!tree2)
	{
		tree1+=tree2;
		tree2=0;
		return;
	}
	if(left==right)
	{
		sgt[tree1].sum+=sgt[tree2].sum;
		DeletePoint(tree2);
		return;
	}
	add_1+=1ll*sgt[sgt[tree1].rson].sum*sgt[sgt[tree2].lson].sum;//通过乘法原理计算逆序对
	add_2+=1ll*sgt[sgt[tree1].lson].sum*sgt[sgt[tree2].rson].sum;
	Merge(sgt[tree1].lson,sgt[tree2].lson,left,MIDDLE,0);//继续合并左右子树
	Merge(sgt[tree1].rson,sgt[tree2].rson,MIDDLE+1,right,0);
	DeletePoint(tree2);
	PushUp(tree1);
}
#undef LSON
#undef RSON
#undef MIDDLE
#undef LEFT
#undef RIGHT
struct Tree
{
	int lson,rson,now;
}tree[MAXN];
int tree_cnt=0;
#define LSON tree[now].lson
#define RSON tree[now].rson
void DFS(int &root)//遍历全树
{
	int val;
	scanf("%d",&val);
	if(val)
	{
		Updata(val,root);//叶节点只要单点修改就好了
		return;
	}
	int now=++tree_cnt;
	DFS(LSON);
	DFS(RSON);
	Merge(LSON,RSON);//非叶节点需要合并子树
	root=LSON;
	answer+=minll(add_1,add_2);//并且加上逆序对更少的情况
}
#undef LSON
#undef RSON
int main()
{
	scanf("%d",&n);
	int root=0;
	DFS(root);
	printf("%lld",answer);
	return 0;
}

「Luogu P3521 [POI2011]ROT-Tree Rotations」

标签:lan   space   ddl   fine   次数   维护   原理   左右   权值线段树   

原文地址:https://www.cnblogs.com/Sxy_Limit/p/12631610.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!