Kalypso創辦新開發工作室 專注於開發《海島大亨7》
阿新 • • 發佈:2021-10-01
Description
Solution
容易想到一個貪心的思路,記得有個叫什麼國王的遊戲的題,大概就是考慮鄰項作差。如果 \(x\) 在前面比 \(y\) 在前面更優的話,一定有
\[b_{x}(a_y+S)+b_yS > b_y(a_x+S)+b_xS \]化簡可得
\[b_xa_y > b_ya_x \]所以最有的情況是按這個排序再依次取。如果擴充套件到樹上的話,就可以用一個堆維護,每次取出最大的。
但是這樣實際上是錯的,因為有依賴關係,即可能有一個點的 \(\frac{b}{a}\) 很小,但它的兒子卻很大。這和藍書上一道染色的題是一個道理。一個性質是,當前所有節點權值最大的點一定會在其父親被選取之後立即被選擇。那麼就可以根據這個倒序更新答案,每次把權值最大的點合併到它的父親,同時計算貢獻。可以用並查集維護。
#include<stdio.h> #include<algorithm> #include<queue> using namespace std; typedef long long ll; inline int read(){ int x=0,flag=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();} return flag? x:-x; } const int N=3e5+7; ll a[N],b[N]; int Fa[N],fa[N],sz[N]; struct Node{ int pos,sz; ll x,y; Node(int pos_=0,int sz_=0,ll x_=0,ll y_=0): pos(pos_),sz(sz_),x(x_),y(y_){} bool operator <(const Node &X) const{ return y*X.x<X.y*x; } }; int find(int x){ if(x==fa[x]) return x; return fa[x]=find(fa[x]); } priority_queue<Node> Q; int main(){ int n=read(); fa[1]=sz[1]=1; for(int i=2;i<=n;i++) Fa[i]=read(); for(int i=1;i<=n;i++){ a[i]=read(),b[i]=read(); if(i!=1) Q.push(Node(i,sz[fa[i]=i]=1,a[i],b[i])); } ll ans=0; while(!Q.empty()){ Node t=Q.top(); Q.pop(); int u=t.pos; if(t.sz!=sz[u]) continue; int x=find(Fa[u]),y=find(u); if(x==y) continue; ans+=a[y]*b[x]; a[x]+=a[y],b[x]+=b[y]; sz[x]+=sz[y],fa[y]=x; if(x!=1) Q.push(Node(x,sz[x],a[x],b[x])); } printf("%lld",ans); }