1. 程式人生 > 其它 >【題解】[POI2011]ROT-Tree Rotations

【題解】[POI2011]ROT-Tree Rotations

Problem

\(\text{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\n",res);
	return 0;
}