1. 程式人生 > >校內訓練0531 逆序對

校內訓練0531 逆序對

out algo string calc mem iostream 整數 數量 close

【題目大意】

有一棵2n-1個節點的二叉樹,它有恰好n個葉子節點,每個葉子節點上寫了一個整數。如果將這棵樹的所有葉子節點上的數從左到右寫下來,便得到一個序列a[1]…a[n]。現在想讓這個序列中的逆序對數量最少,但唯一的操作就是選樹上一個非葉子節點,將它的左右兩顆子樹交換。你可以做任意多次這個操作。求在最優方案下,該序列的逆序對數最少有多少。

n<=200000

【題解】
啊很明顯我們對於每個節點 判斷一下兩邊交換/不交換哪個逆序對貢獻的少就行了

至於這個逆序對貢獻啊?線段樹合並!

啊我不會線段樹合並啊?啟發式合並(dsu on tree)

技術分享
# include <vector>
# include 
<stdio.h> # include <assert.h> # include <string.h> # include <iostream> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10; const int mod = 1e9+7
; # define RG register # define ST static int n, siz, rt, ch[M][2], siz2; int val[M], a[M], m, sz[M], sz2[M]; vector<int> ps; struct BIT { int c[M], n; # define lb(x) (x&(-x)) inline void set(int _n) { n = _n; memset(c, 0, sizeof c); } inline void edt(int
x, int d) { for (; x<=n; x+=lb(x)) c[x] += d; } inline int sum(int x) { int ret=0; for (; x; x-=lb(x)) ret += c[x]; return ret; } inline int sum(int l, int r) { if(l>r) return 0; return sum(r)-sum(l-1); } }T; inline void dfs(int &x) { int t; scanf("%d", &t); if(t == 0) x = ++siz; else x = ++siz2; sz[x] = 1; if(t == 0) { dfs(ch[x][0]); dfs(ch[x][1]); sz[x] += sz[ch[x][0]] + sz[ch[x][1]]; sz2[x] = sz2[ch[x][0]] + sz2[ch[x][1]]; } else { sz2[x] = 1; val[x] = t; ps.push_back(t); } } bool big[M]; ll cura = 0, curb; inline void calc(int x) { if(val[x] != 0) { cura += T.sum(val[x]+1, n); curb += T.sum(val[x]-1); return ; } if(!big[ch[x][0]]) calc(ch[x][0]); if(!big[ch[x][1]]) calc(ch[x][1]); } inline void del(int x, int d) { if(x <= n) { T.edt(val[x], d); return ; } if(!big[ch[x][0]]) del(ch[x][0], d); if(!big[ch[x][1]]) del(ch[x][1], d); } ll ans = 0; inline void dsu(int x, bool kep) { if(x <= n) { if(kep) T.edt(val[x], 1); return; } int bc, sc; if(sz[ch[x][0]] > sz[ch[x][1]]) bc = ch[x][0], sc = ch[x][1]; else bc = ch[x][1], sc = ch[x][0]; dsu(sc, 0); dsu(bc, 1); // printf("x=%d, query=%d\n", x, T.sum(1, n)); big[bc] = 1; cura = curb = 0; calc(x); del(x,1); ans += min(cura, curb); big[bc] = 0; if(kep == 0) del(x,-1); } int main() { cin >> n; siz = n; dfs(rt); T.set(n); sort(ps.begin(), ps.end()); ps.erase(unique(ps.begin(), ps.end()), ps.end()); for (int i=1; i<=siz; ++i) if(val[i] != 0) val[i] = lower_bound(ps.begin(), ps.end(), val[i]) - ps.begin() + 1; // for (int i=1; i<=siz; ++i) printf("x=%d, ls=%d, rs=%d\n", i, ch[i][0], ch[i][1]); dsu(rt, 1); cout << ans << endl; return 0; }
View Code

校內訓練0531 逆序對