1. 程式人生 > >BZOJ2212:[POI2011]Tree Rotation

BZOJ2212:[POI2011]Tree Rotation

淺談線段樹合併:https://www.cnblogs.com/AKMer/p/10251001.html

題目傳送門:https://lydsy.com/JudgeOnline/problem.php?id=2212

遞迴去做,統計每個子樹內最少會產生多少逆序對,在合併線段樹的時候統計就好了。

程式碼如下:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn=2e5+5;

int n,tot;
ll ans,cnt1,cnt2;
int rt[maxn<<1],ls[maxn<<1],rs[maxn<<1];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

struct segment_tree {
    int tot;
    int sum[maxn*20],ls[maxn*20],rs[maxn*20];

    void update(int p) {
        sum[p]=sum[ls[p]]+sum[rs[p]];
    }

    void change(int &p,int l,int r,int pos) {
        p=++tot;
        if(l==r) {sum[p]=1;return;}
        int mid=(l+r)>>1;
        if(pos<=mid)change(ls[p],l,mid,pos);
        else change(rs[p],mid+1,r,pos);
        update(p);
    }

    int merge(int a,int b) {
        if(!a||!b)return a+b;
        cnt1+=1ll*sum[rs[a]]*sum[ls[b]];//cnt1記錄不交換左右子樹會得到多少逆序對
        cnt2+=1ll*sum[rs[b]]*sum[ls[a]];//cnt2記錄交換左右子樹會得到多少逆序對
        ls[a]=merge(ls[a],ls[b]);
        rs[a]=merge(rs[a],rs[b]);
        update(a);return a;
    }
}T;

void solve(int u) {
    int x=read();
    if(x)T.change(rt[u],1,n,x);
    else {
        ls[u]=++tot,solve(ls[u]);
        rs[u]=++tot,solve(rs[u]);
        cnt1=cnt2=0;
        rt[u]=T.merge(rt[ls[u]],rt[rs[u]]);
        ans+=min(cnt1,cnt2);
    }
}

int main() {
    n=read();
    solve(1);
    printf("%lld\n",ans);
    return 0;
}