Bzoj2212:[Poi2011]Tree Rotations:线段树的合并

10 篇文章 0 订阅
4 篇文章 0 订阅

题目链接2212:[Poi2011]Tree Rotations

考虑一个节点的左右子树的子树是否交换过对这个节点的逆序对数目没有影响,只有这个节点直接的子树交换才会产生影响

那么我们可以分治的去计算每个节点的贡献,然后向上传递

每个节点的逆序对是左子树的逆序对的数量+右子树的逆序对数量+跨越子树的逆序对数量

我们交换子树更改的就是最后那个跨越子树的逆序对数量,那么:

如果我们交换了左右子树,跨越子树的逆序对数量为没交换时左子树中<mid的数的数量*右子树中>=mid的数的数量

如果我们没有交换左右子树,那么逆序对数量就是左子树中>=mid的数的数量*右子树中<mid的数的数量

然后我们向上传递信息。这一步用线段树合并即可。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=8000010;
LL ans=0,ans1=0,ans2=0;
int n,r[maxn],root,ls[maxn],a[maxn];
int rs[maxn],s[maxn][2],ind=0,t[maxn];

void init_tree(int &x){
	x=++ind; scanf("%d",&a[x]);
	if (a[x]) return;
	init_tree(ls[x]);
	init_tree(rs[x]);
}

void push_up(int x){
	t[x]=t[s[x][0]]+t[s[x][1]];
}

void insert(int &x,int l,int r,int pos){
	if (!x) x=++ind;
	if (l==r) {t[x]=1;return;}
	int mid=(l+r)>>1;
	if (pos<=mid) insert(s[x][0],l,mid,pos);
	else insert(s[x][1],mid+1,r,pos);
	push_up(x);
}

int merge(int x,int y){
	if (!x) return y; if (!y) return x;
	ans1+=1LL*t[s[x][1]]*t[s[y][0]];
	ans2+=1LL*t[s[x][0]]*t[s[y][1]];
	s[x][0]=merge(s[x][0],s[y][0]);
	s[x][1]=merge(s[x][1],s[y][1]);
	push_up(x); return x;
}

void solve(int x){
	if (a[x]) return;
	solve(ls[x]); solve(rs[x]);
	ans1=ans2=0;
	r[x]=merge(r[ls[x]],r[rs[x]]);
	ans+=min(ans1,ans2);
}

int main(){
	scanf("%d",&n);
	init_tree(root);
	for (int i=1;i<=ind;++i)
	    if (a[i]) insert(r[i],1,n,a[i]);
	solve(root);
	printf("%lld\n",ans);
}


  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值