[机房测试]数据恢复

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);
}
posted @ 2021-10-07 15:05  Kreap  阅读(41)  评论(0编辑  收藏  举报