POI2011 Tree Rotations 2
阿新 • • 發佈:2018-12-12
Tree Rotations 2
POI2011
怎麼可以資料範圍大9倍,記憶體才大1倍啊,對樹套樹說さようなら(再見)吧
題意
1.有一棵二叉樹,所有非葉子節點都有兩個孩子
2.每個葉子節點有一個權值(有n個葉子節點,這些權值是1~n的一個排列)
3.現在可以任意交換每個非葉子節點的左右孩子
4.要求進行一系列交換,使得最終所有葉子節點的權值按照遍歷序寫出來,逆序對個數最少
5.問逆序對個數最少為多少
解
1.啟發式合併Splay 這裡必須按照特別的順序合併,複雜度我也不能證明,總之網上說是 的,意會一下,這樣插入是翻轉的次數應該會比較少吧
一樣是遍歷小樹,但是必須從小到大合併進去
把小樹按照中序遍歷插入到大樹中去
在插入時,可以順便求出有多少個比剛插入的數
大的數(因為剛插入的數被旋到了根,很容易求)
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;
}