bzoj2212 [POI2011]Tree Rotations

传送门

分析

首先我们不难发现交换两棵子树,它们自身的逆序对个数是不变的,改变的只是由一棵子树的x和另一棵子树的y组成的二元组(x,y),所以我们可以考虑使用线段树合并。我们对于每一个叶子节点建一棵权值线段树,然后将他们一一合并,而对于每一个节点是否旋转左右儿子只需要比较这两种情况产生的逆序对个数即可。详见代码。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
long long a[4000100],cnt,son[2][4000100],root,ans,res;
long long CNT,SON[2][4000100],n,rt[4000100],sum[4000100];
inline void addnode(long long &x,long long le,long long ri,long long pl){
      x=++cnt;
      sum[x]++;
      if(le==ri)return;
      long long mid=(le+ri)>>1;
      if(mid>=pl)addnode(son[0][x],le,mid,pl);
        else addnode(son[1][x],mid+1,ri,pl);
      return;
}
inline void build(long long &x){
      x=++CNT;
      scanf("%lld",&a[x]);
      if(a[x]){
          addnode(rt[x],1,n,a[x]);
          return;
      }
      build(SON[0][x]);
      build(SON[1][x]);
      return;
}
inline long long mer(long long x,long long y){
      if(!x)return y;
      if(!y)return x;
      res+=sum[son[0][x]]*sum[son[1][y]];
      son[0][x]=mer(son[0][x],son[0][y]);
      son[1][x]=mer(son[1][x],son[1][y]);
      sum[x]=sum[son[0][x]]+sum[son[1][x]];
      return x;
}
inline void dfs(long long x){
      long long le=SON[0][x],ri=SON[1][x];
      if(a[x])return;
      dfs(le),dfs(ri);
      long long tot=sum[rt[le]]*sum[rt[ri]];
      res=0;
      rt[x]=mer(rt[le],rt[ri]);
      ans+=min(res,tot-res);
      return;
} 
int main(){
      scanf("%lld",&n);
      build(root);
      dfs(root);
      printf("%lld\n",ans);
      return 0;
}
posted @ 2018-08-18 19:18  水题收割者  阅读(148)  评论(0编辑  收藏  举报