Tree Rotations 2
POI2011
怎么可以数据范围大9倍,内存才大1倍啊,对树套树说さようなら(再见)吧
题意
1.有一棵二叉树,所有非叶子节点都有两个孩子
2.每个叶子节点有一个权值(有n个叶子节点,这些权值是1~n的一个排列)
3.现在可以任意交换每个非叶子节点的左右孩子
4.要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少
5.问逆序对个数最少为多少
解
1.启发式合并Splay
这里必须按照特别的顺序合并,复杂度我也不能证明,总之网上说是
n
l
o
g
n
nlog{n}
nlogn的,意会一下,这样插入是翻转的次数应该会比较少吧
一样是遍历小树,但是必须从小到大合并进去
把小树按照中序遍历插入到大树中去
在插入时,可以顺便求出有多少个比刚插入的数
大的数(因为刚插入的数被旋到了根,很容易求)
ans+=min(cnt,sum-cnt);
具体代码
#include<bits/stdc++.h>
using namespace std;
const int M=1000005;
long long ans;
int n,ID,root[M*2];
int fa[M],son[M][2],sz[M];
void rd(int &res) {
res=0;
char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c&15);
while(c=getchar(),c>47);
}
bool get(int x) {
return son[fa[x]][1]==x;
}
void Up(int x) {
sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;
}
void rotate(int x) {
int dir=!get(x),y=fa[x];
son[y][!dir]=son[x][dir];
fa[son[x][dir]]=y;
if(fa[y])son[fa[y]][get(y)]=x;
fa[x]=fa[y];
son[x][dir]=y,fa[y]=x;
Up(y),Up(x);
}
void splay(int x,int rt) {
while(fa[x]!=0) {
int y=fa[x];
if(fa[y]==0)rotate(x);
else {
if(get(x)==get(y))rotate(y);
else rotate(x);
rotate(x);
}
}
root[rt]=x;
}
void insert(int rt,int val) {
int x=root[rt];
for(;; x=son[x][x<val]) {
if(!son[x][x<val])break;
}
son[x][x<val]=val;
fa[val]=x,sz[val]=1;
son[val][0]=son[val][1]=0;
splay(val,rt);
}
int a;
long long cnt,sum;
void merge(int x,int rt) {
if(son[x][0])merge(son[x][0],rt);
int RR=son[x][1];
insert(rt,x);
cnt+=sz[son[root[rt]][1]];
if(RR)merge(RR,rt);
}
void dfs(int x) {
rd(a);
if(a!=0) {
root[x]=a;
fa[a]=0,sz[a]=1;
son[a][0]=son[a][1]=0;
} else {
int p1=++ID;
dfs(p1);
int p2=++ID;
dfs(p2);
if(sz[root[p1]]>sz[root[p2]])swap(p1,p2);
sum=1ll*sz[root[p1]]*sz[root[p2]];
cnt=0;
merge(root[p1],p2);
ans+=min(cnt,sum-cnt);
root[x]=root[p2];
}
}
int main() {
rd(n);
dfs(ID=1);
printf("%lld\n",ans);
return 0;
}