bzoj-2212 Tree Rotations

题意:

给出一个二叉树,叶子结点上有权值;

共有n个叶子结点,权值分别为1-n;

现可将所有结点的左右儿子交换,求最小的逆序对数;

2<=n<=200000;

题解:

这题读入有点鬼畜,但是写起来还是比较优雅的;

考虑到一个结点的子树具体形态与它是否和它的兄弟交换是无关的;

所以我们可以像分治一样,先计算子树之间的最小逆序对数;

然后将所有的权值扔到一个线段树中,向它的父亲传递;

它的父亲是没有权值的,我们只是在这里计算这两个线段树之间最小逆序对数,并将其合并就好了;

具体实现实际上只是一个合并操作中多了两个记录,考虑左子树与右子树是否交换;

对于一个线段树上的结点,我们累加:

左子树<=mid的结点数*右子树>mid的结点数作为左子树与右子树交换的逆序对数;

左子树>mid的结点数*右子树<=mid的结点数作为左子树与右子树不交换的逆序对数;

然后合并完成之后比较一下,累加到答案里就可以了;

答案可能超过int,时间复杂度O(nlogn)吧。。。

内存似乎比较玄学,应该是不超过nlogn的(空间复杂度不会大过时间复杂度呢);

代码:

#include<cctype>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 210000
#define M 3000000
#define LEN 1<<15
#define lson l,mid,ls[no]
#define rson mid+1,r,rs[no]
using namespace std;
typedef long long ll;
int sum[M],ls[M],rs[M],tot;
int st[M],top,n;
ll ans;
char getc()
{
	static char *S,*T,buf[LEN];
	if(S==T)
	{
		T=(S=buf)+fread(buf,1,LEN,stdin);
		if(S==T)
			return EOF;
	}
	return *S++;
}
int read()
{
	static char ch;
	static int D;
	while(!isdigit(ch=getc()));
	for(D=ch-'0';isdigit(ch=getc());)
		D=D*10+ch-'0';
	return D;
}
int newnode()
{
	if(top)	return st[top--];
	return ++tot;
}
void delnode(int no)
{
	st[++top]=no;
	sum[no]=ls[no]=rs[no]=0;
}
void Pushup(int no)
{
	sum[no]=sum[ls[no]]+sum[rs[no]];
}
void Insert(int l,int r,int &no,int val)
{
	if(!no)	no=newnode();
	if(l==r)
		sum[no]++;
	else
	{
		int mid=l+r>>1;
		if(val<=mid)Insert(lson,val);
		else		Insert(rson,val);
		Pushup(no);
	}
}
int merge(int l,int r,int nol,int nor,ll &cntl,ll &cntr)
{
	if(!nol||!nor)	return nol+nor;
	cntl+=(ll)sum[rs[nol]]*sum[ls[nor]];
	cntr+=(ll)sum[ls[nol]]*sum[rs[nor]];
	int mid=l+r>>1,no=newnode();
	ls[no]=merge(l,mid,ls[nol],ls[nor],cntl,cntr);
	rs[no]=merge(mid+1,r,rs[nol],rs[nor],cntl,cntr);
	Pushup(no);
	delnode(nol),delnode(nor);
	return no;
}
int dfs()
{
	int x;
	x=read();
	if(!x)
	{
		int ls=dfs();
		int rs=dfs();
		if(sum[ls]>sum[rs])
			swap(ls,rs);
		ll cntl=0,cntr=0;
		int rt=merge(1,n,ls,rs,cntl,cntr);
		ans+=min(cntl,cntr);
		return rt;
	}
	else
	{
		int rt=0;
		Insert(1,n,rt,x);
		return rt;
	}
}
int main()
{
	n=read();
	dfs();
	printf("%lld\n",ans);
	return 0;
}
时间: 2025-02-01 10:52:36

bzoj-2212 Tree Rotations的相关文章

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

题目链接:BZOJ - 2212 题目分析 子树 x 内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆序对. 左右子树内部的逆序对与是否交换左右子树无关,是否交换左右子树取决于交换后 “跨越 x 左子树与右子树的逆序对” 是否会减小. 因此我们要求出两种情况下的逆序对数,使用线段树合并,对每个节点建一棵线段树,然后合并的同时就求出两种情况下的逆序对. 代码 #include <iostream> #include <cstdli

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 &

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

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

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 (线段树合并)

题目链接: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[

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 2631: tree 动态树+常数优化

2631: tree Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1716  Solved: 576[Submit][Status] Description 一棵n个点的树,每个点的初始权值为1.对于这棵树有q个操作,每个操作为以下四种操作之一:+ u v c:将u到v的路径上的点的权值都加上自然数c:- u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树:* u v c:将u到v的

BZOJ 3282: Tree [LCT]

3282: Tree Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1677  Solved: 744[Submit][Status][Discuss] Description 给定N个点以及每个点的权值,要你处理接下来的M个操作.操作有4种.操作从0到3编号.点从1到N编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和.保证x到y是联通的. 1:后接两个整数(x,y),代表连接x到y,若x到Y已经联通则无需连接.

BZOJ 2631: tree( LCT )

LCT...略麻烦... -------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define rep( i , n ) for( int i = 0 ; i < n ; ++i ) #define