P3521-[POI2011]ROT-Tree【线段树合并】

正题

题目链接:https://www.luogu.com.cn/problem/P3521


题目大意

一棵二叉树,叶子节点有权值,对于每个非叶子节点可以选择交换左右节点,求最后遍历出来的叶子节点权值逆序对最少。


解题思路

十分显然一个节点是否交换是不影响该节点子树之外的逆序对数量的,所以如果更优直接交换就好了。

所以我们开始对于每个叶子节点维护一个线段树,然后每次将两棵线段树合并,顺便在合并的时候统计一下交换优还是不交换优。

时间复杂度 O ( n log n ) O(n\log n)


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=200000;
ll n,ans1,ans2,ans;
struct Seq_Tree{
	#define ls t[x][0]
	#define rs t[x][1]
	ll t[N*30][2],val[N*30],cnt;
	ll Ask(ll x,ll l,ll r,ll L,ll R){
		if(!x) return 0;
		if(l==L&&r==R){return val[x];}
		ll mid=(L+R)/2;
		if(r<=R) return Ask(ls,l,r,L,mid);
		if(l>L) return Ask(rs,l,r,mid+1,R);
		return Ask(ls,l,mid,L,mid)+Ask(rs,mid+1,r,mid+1,R);
	}
	void Change(ll &x,ll pos,ll L,ll R){
		if(!x) x=++cnt;
		if(L==R){val[x]++;return;}
		ll mid=(L+R)/2;
		if(pos<=mid) Change(ls,pos,L,mid);
		else Change(rs,pos,mid+1,R);
		val[x]=val[ls]+val[rs];
		return;
	}
	ll Merge(ll x,ll y,ll L,ll R){
		if(!x||!y) return x+y;
		ans1+=val[rs]*val[t[y][0]];
		ans2+=val[ls]*val[t[y][1]];
		val[x]=val[x]+val[y];
		if(L==R) return x;
		ll mid=(L+R)/2;
		ls=Merge(ls,t[y][0],L,mid);
		rs=Merge(rs,t[y][1],mid+1,R);
		return x;
	}
	#undef ls
	#undef rs
}T;
void dfs(ll &x){
	ll op;
	scanf("%lld",&op);
	if(!op){
		ll ls=0,rs=0;
		dfs(ls);dfs(rs);
		ans1=ans2=0;
		x=T.Merge(ls,rs,1,n);
		ans+=min(ans1,ans2);
	}
	else T.Change(x,op,1,n);
	return;
}
int main()
{
	scanf("%lld",&n);
	ll op=0;dfs(op);
	printf("%lld",ans);
	return 0;
}
发布了857 篇原创文章 · 获赞 55 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/104076170