POJ2054与2018年CCPC桂林A题(集合平均和最大的贪心)

POJ2054的题意:

有一棵n个节点的树,树上每个节点的权值是a[i],要给这棵树涂色,规定涂某个节点前,它的所有父节点都要被涂色,给每个节点涂色花费一单位时间。时间从1开始,假设涂某个点的时间是t[i],总代价是 ∑ i = 0 n a [ i ] ∗ t [ i ] \sum_{i=0}^n a[i]*t[i] i=0na[i]t[i],求最小总代价。( n ≤ 1000 n\le1000 n1000

2018年CCPC桂林A题的题意:

给两个序列A[i],B[i],把它们归并到序列C[i],并要保证在序列C里面A的顺序不变,B的顺序也不变,代价是 ∑ i = 0 n c [ i ] ∗ i \sum_{i=0}^n c[i]*i i=0nc[i]i n ≤ 200000 n\le200000 n200000
那我们把A和B当做两条链并加上根节点就变成POJ2054了。

题解:

如果没有先涂祖先的限制,那么肯定是从大到小涂。有了这个限制后,也应该在涂完父节点后马上涂权值大的节点,这样才能做得和没有限制时更接近。
证明:
父节点是p,大儿子是q,p还有k个小儿子。
先p再q再小儿子们:① C p ∗ T + C q ∗ ( T + 1 ) + ∑ i = 1 k C s o n i ∗ ( T + 1 + i ) Cp*T+Cq*(T+1)+\sum_{i=1}^k Csoni*(T+1+i) CpT+Cq(T+1)+i=1kCsoni(T+1+i)
先p再小儿子们再q:② C p ∗ T + ∑ i = 1 k C s o n i ∗ ( T + i ) + C q ∗ ( T + 2 ) Cp*T+\sum_{i=1}^k Csoni*(T+i)+Cq*(T+2) CpT+i=1kCsoni(T+i)+Cq(T+2)
①-②得 ∑ i = 1 k C s o n i ∗ i − C q ∗ k \sum_{i=1}^k Csoni*i-Cq*k i=1kCsoniiCqk
C q ≥ C s o n i , k ∗ C q ≥ ∑ i = 1 k C s o n i ∗ i Cq\ge Csoni,k*Cq\ge \sum_{i=1}^k Csoni*i CqCsoni,kCqi=1kCsonii
得证

现在我们知道了涂了某个父节点之后要马上涂最大的儿子,可以把它们合并成一个集合,那么有多个集合的时候应该先选哪个呢?
假设集合1:父节点是p,子节点是q
集合2:有k个节点
先1后2:① C p ∗ ( T + 1 ) + C q ∗ ( T + 2 ) + ∑ i = 1 k C i ∗ ( T + 2 + i ) Cp*(T+1)+Cq*(T+2)+\sum_{i=1}^k Ci*(T+2+i) Cp(T+1)+Cq(T+2)+i=1kCi(T+2+i)
先2后1:② C p ∗ ( T + k + 1 ) + C q ∗ ( T + k + 2 ) + ∑ i = 1 k C i ∗ ( T + i ) Cp*(T+k+1)+Cq*(T+k+2)+\sum_{i=1}^k Ci*(T+i) Cp(T+k+1)+Cq(T+k+2)+i=1kCi(T+i)
①-②得 − ( C p + C q ) ∗ k + ∑ i = 1 k C i ∗ 2 -(Cp+Cq)*k+\sum_{i=1}^k Ci*2 (Cp+Cq)k+i=1kCi2
( C p + C q ) / 2 ≥ ( ∑ i = 1 k C i ) / k (Cp+Cq)/2\ge (\sum_{i=1}^k Ci)/k (Cp+Cq)/2(i=1kCi)/k时先1后2
也就是说集合的和的平均值越大的越先。

用优先队列实现带权并查集的合并就可以了。时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define per(i,l,r) for (int i=l;i>=r;i--)
using namespace std;
const int maxn=200500;
struct node{
    double avg; int sum,sz,rt,id;
    friend bool operator <(node a,node b){
        return a.avg<b.avg;
    }
}f[maxn];
priority_queue<node> q;
vector<int> g[maxn];
int fa[maxn],a[maxn];
int n,rt,ans,idx;
void dfs(int u){
    for (int j=0;j<(int)g[u].size();j++){
        int v=g[u][j];
        if (v!=fa[u]) fa[v]=u,dfs(v);
    }
}
int find(int x){
    if (f[x].rt==x) return x;
    return f[x].rt=find(f[x].rt);
}
int main() {
    while (1){
        scanf("%d%d",&n,&rt);
        if (!n&&!rt) break;
        rep(i,1,n) scanf("%d",&a[i]);
        rep(i,1,n) g[i].clear(),fa[i]=0; idx=0; ans=0;
        rep(i,1,n) f[i]=(node){1.0*a[i],a[i],1,i,++idx};
        rep(i,1,n-1) {
            int x,y; scanf("%d%d",&x,&y);
            g[x].push_back(y); g[y].push_back(x);
        }
        dfs(rt);
        rep(i,1,n) q.push(f[i]);
        while (!q.empty()){
            node u=q.top(); q.pop();
            if (u.id!=f[u.rt].id) continue;
            if (!fa[u.rt]) continue;
            ans+=f[find(fa[u.rt])].sz*u.sum;
            int x=find(fa[u.rt]);
            f[u.rt].rt=x;
            f[x].sum+=u.sum,f[x].sz+=u.sz;
            f[x].avg=1.0*f[x].sum/f[x].sz,f[x].id=++idx;
            q.push(f[x]);
        }
        rep(i,1,n) ans+=a[i];
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值