1. 程式人生 > >bzoj 2212 : [Poi2011]Tree Rotations (線段樹合並)

bzoj 2212 : [Poi2011]Tree Rotations (線段樹合並)

sin lse online space 量變 ++ ota hup 左右

題目鏈接:https://www.lydsy.com/JudgeOnline/problem.php?id=2212

思路:用線段樹合並求出交換左右兒子之前之後逆序對的數量,如果數量變小則交換.

實現代碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int M = 4e5+10;
int n,cnt,idx;
ll ans,cnt1,cnt2;
int v[M],l[M],r[M],root[M];
int sum[M*10],ls[M*10],rs[M*10];
void
init_tree(int x){ scanf("%d",&v[x]); if(!v[x]){ l[x] = ++cnt; init_tree(l[x]); r[x] = ++cnt; init_tree(r[x]); } } void pushup(int rt){ sum[rt] = sum[ls[rt]] + sum[rs[rt]]; } void build(int p,int l,int r,int &rt){ if(!rt) rt = ++idx;
if(l == r){ sum[rt] = 1; return ; } int mid = (l + r) >> 1; if(p <= mid) build(p,l,mid,ls[rt]); else build(p,mid+1,r,rs[rt]); pushup(rt); } int merge(int x,int y){ if(!x) return y; if(!y) return x; cnt1 += (ll)sum[rs[x]]*sum[ls[y]]; cnt2
+= (ll)sum[ls[x]]*sum[rs[y]]; ls[x] = merge(ls[x],ls[y]); rs[x] = merge(rs[x],rs[y]); pushup(x); return x; } void solve(int x){ if(!x) return ; solve(l[x]); solve(r[x]); if(!v[x]){ cnt1 = cnt2 = 0; root[x] = merge(root[l[x]],root[r[x]]); ans += min(cnt1,cnt2); } } int main() { scanf("%d",&n); cnt = 1; init_tree(1); for(int i = 1;i <= cnt;i ++){ if(v[i]) build(v[i],1,n,root[i]); } solve(1); printf("%lld\n",ans); return 0; }

bzoj 2212 : [Poi2011]Tree Rotations (線段樹合並)