題目連接:BZOJ - 2212php
子樹 x 內的逆序對個數爲 :x 左子樹內的逆序對個數 + x 右子樹內的逆序對個數 + 跨越 x 左子樹與右子樹的逆序對。ios
左右子樹內部的逆序對與是否交換左右子樹無關,是否交換左右子樹取決於交換後 「跨越 x 左子樹與右子樹的逆序對」 是否會減少。spa
所以咱們要求出兩種狀況下的逆序對數,使用線段樹合併,對每一個節點建一棵線段樹,而後合併的同時就求出兩種狀況下的逆序對。blog
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; inline void Read(int &Num) { char c = getchar(); while (c < '0' || c > '9') c = getchar(); Num = c - '0'; c = getchar(); while (c >= '0' && c <= '9') { Num = Num * 10 + c - '0'; c = getchar(); } } typedef long long LL; inline LL gmin(LL a, LL b) {return a < b ? a : b;} const int MaxN = 400000 + 5, MaxNode = 4000000 + 5; int n, IndexT, Index, RT; int A[MaxN], Tree[MaxN][2], Root[MaxN], T[MaxNode], Son[MaxNode][2]; LL Ans0, Ans1, Ans; void Read_Tree(int &x) { x = ++IndexT; Read(A[x]); if (A[x] != 0) return; Read_Tree(Tree[x][0]); Read_Tree(Tree[x][1]); } inline void Update(int x) { T[x] = T[Son[x][0]] + T[Son[x][1]]; } void Insert(int &x, int s, int t, int Pos) { if (x == 0) x = ++Index; if (s == t) { T[x] = 1; return; } int m = (s + t) >> 1; if (Pos <= m) Insert(Son[x][0], s, m, Pos); else Insert(Son[x][1], m + 1, t, Pos); Update(x); } int Merge(int x, int y) { if (!x) return y; if (!y) return x; Ans0 += (LL)T[Son[x][1]] * (LL)T[Son[y][0]]; Ans1 += (LL)T[Son[x][0]] * (LL)T[Son[y][1]]; Son[x][0] = Merge(Son[x][0], Son[y][0]); Son[x][1] = Merge(Son[x][1], Son[y][1]); Update(x); return x; } void Solve(int x) { if (A[x]) return; Solve(Tree[x][0]); Solve(Tree[x][1]); Ans0 = Ans1 = 0; Root[x] = Merge(Root[Tree[x][0]], Root[Tree[x][1]]); Ans += gmin(Ans0, Ans1); } int main() { scanf("%d", &n); Read_Tree(RT); for (int i = 1; i <= IndexT; ++i) if (A[i] != 0) Insert(Root[i], 1, n, A[i]); Solve(RT); cout << Ans << endl; return 0; }