【BZOJ2212】[POI2011]Tree Rotations (线段树合并)
【BZOJ2212】[POI2011]Tree Rotations (线段树合并)
题面
题解
因为是一棵二叉树,我们发现对于左右儿子而言只有两种放法。
不考虑左右儿子内部的相对顺序,那么发现两个儿子先后顺序的逆序对数是固定的,而确定好顺序之后显然就是一个分治的过程。
那么显然每次决策都是选择放在前面后,左右之间贡献的逆序对数较少的那一边。
考虑这个过程如何实现,从上往下做显然不好做。
考虑从下往上合并,显然启发式合并是可行的,但是这样复杂度会多出一个\(log\)。我们发现可以在线段树合并的过程中维护逆序对,这样子复杂度就变成了一个\(log\)。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 400200
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Node{int ls,rs,v;}t[MAX<<5];
int rt[MAX],tot;
void Modify(int &x,int l,int r,int p)
{
if(!x)x=++tot;t[x].v+=1;if(l==r)return;
int mid=(l+r)>>1;
if(p<=mid)Modify(t[x].ls,l,mid,p);
else Modify(t[x].rs,mid+1,r,p);
}
ll tao1,tao2,ans;
void Merge(int &x,int y)
{
if(!x||!y){x|=y;return;}
t[x].v+=t[y].v;
tao1+=1ll*t[t[x].ls].v*t[t[y].rs].v;
tao2+=1ll*t[t[x].rs].v*t[t[y].ls].v;
Merge(t[x].ls,t[y].ls);
Merge(t[x].rs,t[y].rs);
}
int node=0,n;
int ch[MAX][2],V[MAX];
int gettree()
{
int x=read(),u=++node;
if(!x)ch[u][0]=gettree(),ch[u][1]=gettree();
else V[u]=x;
return u;
}
void dfs(int u)
{
if(V[u]){Modify(rt[u],1,n,V[u]);return;}
dfs(ch[u][0]);dfs(ch[u][1]);tao1=tao2=0;
Merge(rt[u]=rt[ch[u][0]],rt[ch[u][1]]);
ans+=min(tao1,tao2);
}
int main()
{
n=read();gettree();
dfs(1);
printf("%lld\n",ans);
return 0;
}