1. 程式人生 > >Luogu3521/BZOJ2212 POI2011 Tree rotations 線段樹合併

Luogu3521/BZOJ2212 POI2011 Tree rotations 線段樹合併

傳送門——Luogu

傳送門——BZOJ


葉子節點的權值兩兩不同,考慮線段樹合併進行統計。

首先有一個顯然的貪心策略:如果在某一個節點處,交換左右子樹會使得這一棵子樹內的逆序對個數更少,就一定要交換,因為現在交不交換對以後的狀態不會產生影響。

然後我們考慮如何計算交換或者不交換的逆序對數量。對於一棵子樹中的一個數來說,以它為右端的逆序對數就是另一棵子樹中比它大的數,這類似於線段樹中的查詢字首和的操作。這種統計操作也是線段樹合併很擅長的。

具體程式碼段如下

int merge(int p , int q){
    if(!p)
        return q;
    
if(!q) return p; Tree[p].size += Tree[q].size; ans1 += 1ll * Tree[Tree[p].l].size * Tree[Tree[q].r].size; ans2 += 1ll * Tree[Tree[q].l].size * Tree[Tree[p].r].size; //ans1表示交換,ans2表示不交換 Tree[p].l = merge(Tree[p].l , Tree[q].l); Tree[p].r = merge(Tree[p].r , Tree[q].r); throwaway(q);
//節點回收 return p; }

中間的ans1和ans2的統計應該也是不難理解的。

 

#include<bits/stdc++.h>
#define mid ((l + r) >> 1)
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    bool f = 0;
    char c = getchar();
    while(c != EOF && !isdigit(c)){
        if
(c == '-') f = 1; c = getchar(); } while(c != EOF && isdigit(c)){ a = (a << 3) + (a << 1) + (c ^ '0'); c = getchar(); } return f ? -a : a; } const int MAXN = 200010; struct node{ int l , r , size; }Tree[MAXN * 20]; int root[MAXN << 1] , N , cntNode , cnt; long long ans , ans1 , ans2; queue < int > trashbin; inline int allocate(){ if(trashbin.empty()) return ++cntNode; int t = trashbin.front(); trashbin.pop(); return t; } inline void throwaway(int now){ Tree[now].l = Tree[now].r = Tree[now].size = 0; trashbin.push(now); } void insert(int& now , int l , int r , int tar){ if(!now) now = allocate(); ++Tree[now].size; if(l != r) if(mid >= tar) insert(Tree[now].l , l , mid , tar); else insert(Tree[now].r , mid + 1 , r , tar); } int merge(int p , int q){ if(!p) return q; if(!q) return p; Tree[p].size += Tree[q].size; ans1 += 1ll * Tree[Tree[p].l].size * Tree[Tree[q].r].size; ans2 += 1ll * Tree[Tree[q].l].size * Tree[Tree[p].r].size; Tree[p].l = merge(Tree[p].l , Tree[q].l); Tree[p].r = merge(Tree[p].r , Tree[q].r); throwaway(q); return p; } void create(int now){ int t = read(); if(!t){ int p = ++cnt; create(p); root[now] = root[p]; create(p = ++cnt); merge(root[now] , root[p]); ans += min(ans1 , ans2); ans1 = ans2 = 0; } else insert(root[now] , 1 , N , t); } int main(){ #ifndef ONLINE_JUDGE freopen("3521.in" , "r" , stdin); //freopen("3521.out" , "w" , stdout); #endif N = read(); create(1); printf("%lld" , ans); return 0; }