P3521 [POI2011]ROT-Tree Rotations

题目大意:

给一棵\((1≤n≤200000)\)个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少。

我们发现交换两个子树并不会影响某个子树内的逆序对个数,只会对两个子树之间的逆序对产生影响.

所以我们将换与不换的逆序对求出来,取个最小值

将两颗线段树合并就好了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cctype>
#include<algorithm>
#define LL long long
using namespace std;
const int N = 4e5 + 3;
struct node{
    int sum;
    int lc,rc;  
}a[N * 30];
int root;
int n,t,cnt = 1;
int rt[N];
LL ans1,ans2,ans;
inline int read(){
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') c = -1;
        ch = getchar(); 
    }
    while(isdigit(ch)){
        v = v * 10 + ch - 48;
        ch = getchar(); 
    }
    return v * c;
}
inline void pushup(int u){
    a[u].sum = a[a[u].lc].sum + a[a[u].rc].sum;
}
inline void ins(int &u,int l,int r,int x){
//  printf("%d %d %d\n",l,r,x);
    if(!u) u = ++t; 
    if(l == r){
        a[u].sum++;
        return ;    
    }
    int mid = (l + r) >> 1;
    if(x <= mid) ins(a[u].lc,l,mid,x);
    else ins(a[u].rc,mid + 1,r,x);
    pushup(u);
}
inline void merge(int &u1,int u2,int l,int r){
    if(!u1){u1 = u2;return ;}
    if(!u2) return ;
    if(l == r){
        a[u1].sum += a[u2].sum;
        return ;
    }
    int mid = (l + r) >> 1;
    ans1 += 1ll * a[a[u1].lc].sum * a[a[u2].rc].sum;
    ans2 += 1ll * a[a[u2].lc].sum * a[a[u1].rc].sum;
    merge(a[u1].lc,a[u2].lc,l,mid);
    merge(a[u1].rc,a[u2].rc,mid + 1,r);
    pushup(u1); 
}
inline void solve(int pos){
    int x = read();
    if(!x){
        int lc = ++cnt;solve(lc);
        int rc = ++cnt;solve(rc);
        merge(rt[pos],rt[lc],1,n);
        ans1 = ans2 = 0;
        merge(rt[pos],rt[rc],1,n);
        ans += min(ans1,ans2);
    }
    else{
        ins(rt[pos],1,n,x);
        return ;    
    }
}
int main(){
    n = read();
    solve(1);
    printf("%lld\n",ans);
    return 0;   
}