BZOJ 2212: [Poi2011]Tree Rotations(线段树合并)

传送门

解题思路

  线段树合并,考虑交换两个子树时,对除这两棵子树外的其余点的逆序对不会造成影响,所以我们只需要贪心的使这两棵子树产生的逆序对最小。而考虑时我们也只需要考虑两棵子树间的逆序对数,不需要考虑每棵子树内部逆序对数,这样就非常好算了,可以线段树合并,合并的同时统计一下交换前和后的逆序对数,然后取个\(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;	
}
posted @ 2019-02-03 15:37  Monster_Qi  阅读(98)  评论(0编辑  收藏  举报