【洛谷 P4437】 [HNOI/AHOI2018]排列(贪心,堆)

题目链接
如果\(j<=k,a_{p[j]}!=p[k]\)可以理解为如果\(a_{p[j]}=p[k]\),那么\(k\)一定要放在\(j\)前面,也就是\(a_j\)\(j\)前面。
于是连边\((a[j],j)\),表示\(a[j]\)\(j\)前面,如果有环就是无解,如果没有环那么一定是一棵以\(0\)为根的树
根据题意,我们肯定是要尽量把\(w\)小的安排在前面。
于是考虑这样一个贪心:
对于当前最小\(w_i\)
如果\(fa[i]=0\),那么肯定直接选\(i\)
如果\(fa[i]!=0\),那么当选完\(fa[i]\)后肯定马上选\(i\)
于是就可以把\(fa[i]\)\(i\)合并了。
合并了以后显然不能按权值和排序,而要按权值的平均值。
维护当前最小可以用堆,
但合并后要修改权值,可以用平板电视的带修改堆,或者标记一下有没有被合并。
统计答案类似秦九韶定理。

#include <cstdio>
#include <queue>
using namespace std;
inline int read(){
    int s = 0, w = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); }
    return s * w;
}
const int MAXN = 500010;
struct Edge{
    int next, to;
}e[MAXN];
int head[MAXN], num;
inline void Add(int from, int to){
    e[++num].to = to; e[num].next = head[from]; head[from] = num;
}
struct node{
    int size, id;
    long long val;
    int operator > (const node A) const{
        return val * A.size > A.val * size;
    }
}now;
int n, p;
long long ans;
int vis[MAXN], f[MAXN], size[MAXN], fa[MAXN];
long long w[MAXN];
priority_queue <node, vector<node>, greater<node> > q;
int dfs(int u){
    vis[u] = 1;
    for(int i = head[u]; i; i = e[i].next)
       if(vis[e[i].to])
         return 1;
       else
         if(dfs(e[i].to)) return 1;
    return 0;
}
int find(int x){
    return f[x] == x ? x : f[x] = find(f[x]);
}
int main(){
    n = read(); size[0] = 1;
    for(int i = 1; i <= n; ++i)
       Add(fa[i] = read(), f[i] = i);
    if(!head[0] || dfs(0)){ printf("-1"); return 0; }
    for(int i = 1; i <= n; ++i){
       w[i] = read();
       q.push( (node){ size[i] = 1, i, w[i] } );
    }
    while(q.size()){
        now = q.top(); q.pop();
        if(size[now.id] ^ now.size) continue;
        f[now.id] = p = find(fa[now.id]); ans += w[now.id] * size[p];
        size[p] += size[now.id]; w[p] += w[now.id];
        if(p) q.push((node){ size[p], p, w[p] });
    }
    printf("%lld\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/Qihoo360/p/10382313.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值