1. 程式人生 > >POI2011 Tree Rotations 2

POI2011 Tree Rotations 2

Tree Rotations 2

POI2011
怎麼可以資料範圍大9倍,記憶體才大1倍啊,對樹套樹說さようなら(再見)吧

題意

1.有一棵二叉樹,所有非葉子節點都有兩個孩子

2.每個葉子節點有一個權值(有n個葉子節點,這些權值是1~n的一個排列)

3.現在可以任意交換每個非葉子節點的左右孩子

4.要求進行一系列交換,使得最終所有葉子節點的權值按照遍歷序寫出來,逆序對個數最少

5.問逆序對個數最少為多少

1.啟發式合併Splay 這裡必須按照特別的順序合併,複雜度我也不能證明,總之網上說是 nlognnlog{n}的,意會一下,這樣插入是翻轉的次數應該會比較少吧

一樣是遍歷小樹,但是必須從小到大合併進去 把小樹按照中序遍歷插入到大樹中去

在插入時,可以順便求出有多少個比剛插入的數大的數(因為剛插入的數被旋到了根,很容易求) 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; }