BZOJ 2212: [Poi2011]Tree Rotations(線段樹合並)
阿新 • • 發佈:2019-02-03
對數 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(線段樹合並)