1. 程式人生 > >BZOJ 2212: [Poi2011]Tree Rotations(線段樹合並)

BZOJ 2212: [Poi2011]Tree Rotations(線段樹合並)

對數 long long ios php update mes 其余 etc name

傳送門

解題思路

  線段樹合並,考慮交換兩個子樹時,對除這兩棵子樹外的其余點的逆序對不會造成影響,所以我們只需要貪心的使這兩棵子樹產生的逆序對最小。而考慮時我們也只需要考慮兩棵子樹間的逆序對數,不需要考慮每棵子樹內部逆序對數,這樣就非常好算了,可以線段樹合並,合並的同時統計一下交換前和後的逆序對數,然後取個\(min\)加到答案裏。

代碼

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;
const int N=200005;
typedef long long LL;

inline int rd(){
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) f=ch==‘-‘?9:1,ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-‘0‘,ch=getchar();
    return f?x:-x;  
}

int n,tot,sum[N<<5],ls[N<<5],rs[N<<5];
LL ans,res1,res2;

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

int merge(int u,int v,int l,int r){
    if(!u || !v) return (u|v);
    if(l==r) {sum[++tot]=sum[u]+sum[v]; return tot;}
    int mid=(l+r)>>1;   
    res1+=(LL)sum[rs[u]]*sum[ls[v]],res2+=(LL)sum[ls[u]]*sum[rs[v]];
    ls[u]=merge(ls[u],ls[v],l,mid); rs[u]=merge(rs[u],rs[v],mid+1,r);
    sum[u]+=sum[v]; return u;
}

int DFS(){
    int now=rd(),tmp,LS,RS;
    if(now) return update(1,n,now);
    LS=DFS(); RS=DFS(); tmp=merge(LS,RS,1,n);
    ans+=min(res1,res2); res1=res2=0;
    return tmp;
}

int main(){
    n=rd(); DFS();
    printf("%lld",ans);
    return 0;   
}

BZOJ 2212: [Poi2011]Tree Rotations(線段樹合並)