「Luogu P3521 [POI2011]ROT-Tree Rotations」

题目大意

给出一颗有 \(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;
}

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

时间: 2024-10-13 06:19:51

「Luogu P3521 [POI2011]ROT-Tree Rotations」的相关文章

Luogu P3521 [POI2011]ROT-Tree Rotations

题目链接 \(Click\) \(Here\) 线段树合并,没想到学起来意外的很简单,一般合并权值线段树. 建树方法和主席树一致,即动态开点.合并方法类似于\(FHQ\)的合并,就是把两棵树的信息整合到一个里面.暂时没写过定义域不同的线段树合并,具体方法也想象不出来,写到了再详细讲吧. 算法复杂度:均摊\(O(NlogN)\),实际空间时间复杂度都不够稳定,需要谨慎使用. #include <bits/stdc++.h> using namespace std; const int N = 2

BZOJ2212: [Poi2011]Tree Rotations

2212: [Poi2011]Tree Rotations Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 391  Solved: 127[Submit][Status] Description Byteasar the gardener is growing a rare tree called Rotatus Informatikus. It has some interesting features: The tree consists o

POI2011 Tree Rotations

POI2011 Tree Rotations 给定一个n<=2e5个叶子的二叉树,可以交换每个点的左右子树.要求前序遍历叶子的逆序对最少. 由于对于当前结点x,交换左右子树,对于范围之外的逆序对个数并没有影响,所以可以进行线段树合并,合并时统计l在左边还是在右边更优. #include <cstdio> #include <cctype> using namespace std; typedef long long LL; inline void read(int &

P3521 [POI2011]ROT-Tree Rotations

P3521 [POI2011]ROT-Tree Rotations 本题可以通过合并数据结构解决. 权值线段树合并的时间复杂度为O(nlogn). 证明: • n个节点相互独立. • 考虑合并节点的意义:两棵线段树在当前区间内都有值且新的树在当前区间的值相对原来两棵树的值都增加了. • 说明对于一个线段树区间,merge时访问到它的次数不会超过该区间的长度大小次. • 那么显然总访问次数的上限为 nlogn. 1 #include<bits/stdc++.h> 2 #define ll lon

SSH连接时出现「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」解决办法

用ssh來操控github,沒想到連線時,出現「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」,後面還有一大串英文,這時當然要向Google大神求助啦!收尋了一下,終於被小弟找到原因了- @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@

线段树合并(【POI2011】ROT-Tree Rotations)

线段树合并([POI2011]ROT-Tree Rotations) 题意 现在有一棵二叉树,所有非叶子节点都有两个孩子.在每个叶子节点上有一个权值(有nn个叶子节点,满足这些权值为1-n1-n的一个排列).可以任意交换每个非叶子节点的左右孩子. 要求进行一系列交换,使得最终所有叶子节点的权值按照前序遍历序写出来,逆序对个数最少. 解法 我们对每一个叶子节点建立一颗权值线段树,然后,我们考虑将两个叶子节点上的线段树合并起来,然后我们考虑逆序对的个数. 如果我们将左儿子的线段树放在前面,则产生的逆

七牛云荣获「2018 中国云计算产业领军企业」称号

12 月 20 日,以「驾驭数字化转型走进智能互联时代」为主题的 2018 中国软件大会暨首届 CIO 创新峰会在北京新世纪日航饭店举行. 作为引领中国软件和信息技术服务产业发展的风向标和产业年度盛典,会上,隆重颁发了「2018 中国云计算产业领军企业」奖项.七牛云受邀出席,凭借其在云计算服务领域取得的优异成绩,荣获「2018 中国云计算产业领军企业」称号. 七牛云荣获「2018中国云计算产业领军企业」称号 中国软件大会自创办以来,一直紧跟时代步伐,把握行业热点,保持着高水准的思想内容和前沿观察

「十二省联考 2019」字符串问题

「十二省联考 2019」字符串问题 解题思路 傻逼题.. 考虑问题转化为一个A串向其支配的所有B串的后缀A串连边,如果有环答案 \(-1\) 否则是这个 \(\text{DAG}\) 上最长路径,直接建图是 \(n^2\) 的,考虑优化建图即可. 由于 \(A,B\) 都是原串的一个子串,那么对原串的反串建 SAM,一个子串的后缀就是其所在节点上比它长的串以及,其子树里的所有串. 首先将所有 \(A,B\) 串在 SAM上用倍增定位并新建节点,把SAM上每个节点拆成入点和出点,对于SAM每一个节

「十二省联考 2019」字符串问题 解题报告

「十二省联考 2019」字符串问题 当场就去世了,我这菜人改了一下午 考虑一个A,B之间的连边实际表示了两个A之间的有向边,然后把A的连边处理好,就转成了拓扑排序找环+最长链 但是边数很多,考虑优化连边 A,B之间的连边显然没法优化的,考虑一个B可以表示所有它的后缀A 把串反向建出SAM,然后一个B的后缀就是par树的子树 可以拿倍增定位 好了这题就没了 注意到一个事情,定位的点可能重复,于是对SAM拆点,每个点挂一个vector表示一个A或者B的点在SAM的这个位置 然后考虑如何连边 一个B所