【题解】[POI2011]ROT-Tree Rotations

Problem

( ext{Solution:})

这东西的输入有些新奇,写一个递归函数即可。

观察到题目要求的是子树交换,没有修改。而且,显然地,这样的结构必然要满足对于它每一个子结构都达到最优。而且任意一棵子树内部交换对答案的影响是独立的。

考虑从下往上维护答案,每次需要合并两棵子树的信息并选择小的逆序对。

合并信息,自然想到了线段树合并。

考虑如何在线段树合并的时候计算出逆序对:用类似于 cdq分治 的思想,考虑计算跨越区间的答案:用位置靠前的节点数乘位置靠后的节点数,并同时满足前面的数小于后面的。然后再分别递归处理子树(左右区间)。

这样就可以做到一只 $log $ 了。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e6+10;
int n,root,rt[MAXN],cnt;
int ls[MAXN],rs[MAXN],sum[MAXN];
int node,pa[MAXN],tot,head[MAXN];
long long K1,K2,ans[MAXN];
inline int read(){
	int s=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch)){
		s=s*10-48+ch;
		ch=getchar();
	}
	return s;
}
inline long long Min(long long x,long long y){return x<y?x:y;}
struct E{int nxt,to;}e[MAXN];
inline void add(int x,int y){e[++tot]=(E){head[x],y};head[x]=tot;}
inline void pushup(int x){sum[x]=sum[ls[x]]+sum[rs[x]];}
void change(int &x,int L,int R,int pos,int v){
	if(!x)x=++node;
	if(L==R){
		sum[x]+=v;
		return;
	}
	int mid=(L+R)>>1;
	if(pos<=mid)change(ls[x],L,mid,pos,v);
	else change(rs[x],mid+1,R,pos,v);
	pushup(x);
}
int merge(int x,int y,int l,int r,int pos){
	if(!x||!y)return x+y;
	if(l==r){sum[x]+=sum[y];return x;}
	int mid=(l+r)>>1;
	K1+=1ll*sum[rs[x]]*sum[ls[y]];
	K2+=1ll*sum[ls[x]]*sum[rs[y]];
	ls[x]=merge(ls[x],ls[y],l,mid,pos);
	rs[x]=merge(rs[x],rs[y],mid+1,r,pos);
	pushup(x);return x;
}
void Read(int &x){
	if(!x)x=++cnt;
	int v=read();
	if(v==0){
		int lson=0;
		Read(lson);
		int rson=0;
		Read(rson);
		pa[lson]=pa[rson]=x;
		add(lson,x);add(rson,x);
		add(x,lson);add(x,rson);
	}
	else {
		change(rt[x],1,n,v,1);
		ans[x]=0;
		return;
	}
}
void dfs(int x){
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].to;
		if(j==pa[x])continue;
		dfs(j);
		K1=K2=0;
		rt[x]=merge(rt[x],rt[j],1,n,x);
		ans[x]=Min(K1,K2);
	}
}
int main(){
	n=read();
	Read(root);
	dfs(root);
	long long res=0;
	for(int i=1;i<=cnt;++i)res+=ans[i];
	printf("%lld
",res);
	return 0;
}
原文地址:https://www.cnblogs.com/h-lka/p/14956023.html