BZOJ2212:[POI2011]Tree Rotation
阿新 • • 發佈:2019-01-10
淺談線段樹合併:https://www.cnblogs.com/AKMer/p/10251001.html
題目傳送門:https://lydsy.com/JudgeOnline/problem.php?id=2212
遞迴去做,統計每個子樹內最少會產生多少逆序對,在合併線段樹的時候統計就好了。
程式碼如下:
#include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const int maxn=2e5+5; int n,tot; ll ans,cnt1,cnt2; int rt[maxn<<1],ls[maxn<<1],rs[maxn<<1]; int read() { int x=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0'; return x*f; } struct segment_tree { int tot; int sum[maxn*20],ls[maxn*20],rs[maxn*20]; void update(int p) { sum[p]=sum[ls[p]]+sum[rs[p]]; } void change(int &p,int l,int r,int pos) { p=++tot; if(l==r) {sum[p]=1;return;} int mid=(l+r)>>1; if(pos<=mid)change(ls[p],l,mid,pos); else change(rs[p],mid+1,r,pos); update(p); } int merge(int a,int b) { if(!a||!b)return a+b; cnt1+=1ll*sum[rs[a]]*sum[ls[b]];//cnt1記錄不交換左右子樹會得到多少逆序對 cnt2+=1ll*sum[rs[b]]*sum[ls[a]];//cnt2記錄交換左右子樹會得到多少逆序對 ls[a]=merge(ls[a],ls[b]); rs[a]=merge(rs[a],rs[b]); update(a);return a; } }T; void solve(int u) { int x=read(); if(x)T.change(rt[u],1,n,x); else { ls[u]=++tot,solve(ls[u]); rs[u]=++tot,solve(rs[u]); cnt1=cnt2=0; rt[u]=T.merge(rt[ls[u]],rt[rs[u]]); ans+=min(cnt1,cnt2); } } int main() { n=read(); solve(1); printf("%lld\n",ans); return 0; }