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;
}

深深地感到自己的弱小。

posted @ 2020-06-17 12:09  syzf2222  阅读(189)  评论(0编辑  收藏  举报