P3521 [POI2011]ROT-Tree Rotations
如果不算数组开小和没开\(long long\)的话,我又是一遍过的。
思路很简单,考虑在线段树合并的时候,计算逆序对的贡献。
假设合并线段树\(a\)和\(b\),则在区间\(\left[l,r\right]\)的时候,
\(a\)在前所产生的逆序对个数为\(a\)的右子树的大小乘以\(b\)的左子树的大小。
\(b\)在前所产生的逆序对个数为\(a\)的左子树的大小乘以\(b\)的右子树的大小。
思想类似于\(cdq\)分治。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=4e5+10;
int n,a[maxn],root,cnt,tot,ans;
int lson[maxn<<1],rson[maxn<<1],rt[maxn<<1];
struct node{
int val,lc,rc;
}tr[maxn*20];
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
inline int init(){
int x=read(),lc,rc;
if(x){a[++cnt]=x;return cnt;}
lc=init();rc=init();
++cnt;lson[cnt]=lc;rson[cnt]=rc;
return cnt;
}
inline int update(int h,int l,int r,int x){
if(!h)h=++tot;
tr[h].val++;
if(l==r)return h;
int mid=(l+r)>>1;
if(mid>=x)tr[h].lc=update(tr[h].lc,l,mid,x);
else tr[h].rc=update(tr[h].rc,mid+1,r,x);
return h;
}
int cnt1,cnt2,tot1,tot2;
inline int merge(int a,int b,int l,int r){
if(!a)return b;
if(!b)return a;
cnt1+=tr[tr[a].lc].val*tr[tr[b].rc].val;
cnt2+=tr[tr[a].rc].val*tr[tr[b].lc].val;
tr[a].val+=tr[b].val;
if(l==r)return a;
int mid=(l+r)>>1;
tr[a].lc=merge(tr[a].lc,tr[b].lc,l,mid);
tr[a].rc=merge(tr[a].rc,tr[b].rc,mid+1,r);
return a;
}
inline void dfs(int x){
if(!lson[x]){
rt[x]=update(rt[x],1,n,a[x]);
return;
}
dfs(lson[x]);
dfs(rson[x]);
cnt1=cnt2=tot1=tot2=0;
rt[x]=merge(rt[lson[x]],rt[rson[x]],1,n);
ans+=min(cnt1,cnt2);
}
signed main(){
n=read();
root=init();
dfs(root);
printf("%lld\n",ans);
return 0;
}
深深地感到自己的弱小。