【題解】[POI2011]ROT-Tree Rotations
阿新 • • 發佈:2021-06-30
\(\text{Solution:}\)
這東西的輸入有些新奇,寫一個遞迴函式即可。
觀察到題目要求的是子樹交換,沒有修改。而且,顯然地,這樣的結構必然要滿足對於它每一個子結構都達到最優。而且任意一棵子樹內部交換對答案的影響是獨立的。
考慮從下往上維護答案,每次需要合併兩棵子樹的資訊並選擇小的逆序對。
合併資訊,自然想到了線段樹合併。
考慮如何線上段樹合併的時候計算出逆序對:用類似於 cdq分治 的思想,考慮計算跨越區間的答案:用位置靠前的節點數乘位置靠後的節點數,並同時滿足前面的數小於後面的。然後再分別遞迴處理子樹(左右區間)。
這樣就可以做到一隻 $\log $ 了。
#include<bits/stdc++.h> using namespace std; const int MAXN=5e6+10; int n,root,rt[MAXN],cnt; int ls[MAXN],rs[MAXN],sum[MAXN]; int node,pa[MAXN],tot,head[MAXN]; long long K1,K2,ans[MAXN]; inline int read(){ int s=0; char ch=getchar(); while(!isdigit(ch))ch=getchar(); while(isdigit(ch)){ s=s*10-48+ch; ch=getchar(); } return s; } inline long long Min(long long x,long long y){return x<y?x:y;} struct E{int nxt,to;}e[MAXN]; inline void add(int x,int y){e[++tot]=(E){head[x],y};head[x]=tot;} inline void pushup(int x){sum[x]=sum[ls[x]]+sum[rs[x]];} void change(int &x,int L,int R,int pos,int v){ if(!x)x=++node; if(L==R){ sum[x]+=v; return; } int mid=(L+R)>>1; if(pos<=mid)change(ls[x],L,mid,pos,v); else change(rs[x],mid+1,R,pos,v); pushup(x); } int merge(int x,int y,int l,int r,int pos){ if(!x||!y)return x+y; if(l==r){sum[x]+=sum[y];return x;} int mid=(l+r)>>1; K1+=1ll*sum[rs[x]]*sum[ls[y]]; K2+=1ll*sum[ls[x]]*sum[rs[y]]; ls[x]=merge(ls[x],ls[y],l,mid,pos); rs[x]=merge(rs[x],rs[y],mid+1,r,pos); pushup(x);return x; } void Read(int &x){ if(!x)x=++cnt; int v=read(); if(v==0){ int lson=0; Read(lson); int rson=0; Read(rson); pa[lson]=pa[rson]=x; add(lson,x);add(rson,x); add(x,lson);add(x,rson); } else { change(rt[x],1,n,v,1); ans[x]=0; return; } } void dfs(int x){ for(int i=head[x];i;i=e[i].nxt){ int j=e[i].to; if(j==pa[x])continue; dfs(j); K1=K2=0; rt[x]=merge(rt[x],rt[j],1,n,x); ans[x]=Min(K1,K2); } } int main(){ n=read(); Read(root); dfs(root); long long res=0; for(int i=1;i<=cnt;++i)res+=ans[i]; printf("%lld\n",res); return 0; }