P3521 [POI2011]ROT-Tree Rotations - 線段樹合併
阿新 • • 發佈:2021-06-19
題解
考慮對於每個點都開一棵動態開點線段樹,以點權為下標、個數為值,記錄以這個點為根的子樹內的資訊。
可以發現交換某個點 \(u\) 的代價可以快速計算:在把左、右兒子的線段樹合併上來的時候,對於這兩棵線段樹上的對應點 \(p,q\),不交換的代價會增加 \(v_{\mathrm{rson}(p)}\times v_{\mathrm{lson}(q)}\),交換的代價會增加 \(v_{\mathrm{lson}(p)}\times v_{\mathrm{rson}(q)}\)。因為每個點選擇是否交換對其祖先節點並無影響,所以每個點都貪心地選擇代價最小的方案即可。
程式碼
#include <cstdio> #include <cstring> #include <cctype> #include <vector> using namespace std; #define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti) #define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti) template<typename T> void Read(T &x){ x=0;int _f=1; char ch=getchar(); while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar(); while(isdigit(ch)) x=x*10+(ch^48),ch=getchar(); x=x*_f; } template<typename T,typename... Args> void Read(T &x,Args& ...others){ Read(x);Read(others...); } typedef long long ll; const int N=6e5+5; int n,leaf,son[N][2],w[N]; int ReadTree(){ int x,u=++n;Read(x); if(x) w[u]=x; else{ son[u][0]=ReadTree();son[u][1]=ReadTree(); } return u; } #define ls(xx) t[xx].ls #define rs(xx) t[xx].rs int tot=0; struct Node{ int l,r,ls,rs,v; }t[N*18]; int Newnode(int l,int r){ t[++tot]=Node{l,r,0,0,0}; return tot; } void Pushup(int p){t[p].v=t[ls(p)].v+t[rs(p)].v;} void Add(int p,int pos,int x){ if(t[p].l==t[p].r){ t[p].v+=x;return; } int mid=(t[p].l+t[p].r)>>1; if(pos<=mid){ if(!ls(p)) ls(p)=Newnode(t[p].l,mid); Add(ls(p),pos,x); }else{ if(!rs(p)) rs(p)=Newnode(mid+1,t[p].r); Add(rs(p),pos,x); } Pushup(p); } int Merge(int u,int v,ll &x,ll &y){ if(!u||!v) return u^v; if(t[u].l==t[u].r){t[u].v+=t[v].v;return u;} x+=1LL*t[rs(u)].v*t[ls(v)].v,y+=1LL*t[rs(v)].v*t[ls(u)].v; ls(u)=Merge(ls(u),ls(v),x,y),rs(u)=Merge(rs(u),rs(v),x,y); Pushup(u);return u; } ll Solve(int u,int &rt){ if(w[u]){ rt=Newnode(1,leaf); Add(rt,w[u],1);return 0; } int rt1,rt2;ll x=0,y=0,res=0; res+=Solve(son[u][0],rt1);res+=Solve(son[u][1],rt2); rt=Merge(rt1,rt2,x,y); return res+min(x,y); } int main(){ Read(leaf);ReadTree(); int temp; printf("%lld\n",Solve(1,temp)); return 0; }