• [HNOI/AHOI2018]排列



    题解

    堆 + 贪心
    首先题面写的肥肠的隐晦
    说白了题面的意思就是给你一棵树
    (a[u])代表每个点的父节点(fa[u])
    然后题目的意思就是选择这个点必须选择先选择了这个点的父亲,第(i)个被选择的点的贡献是(i imes w)
    设当前已经选择了(x)个点了,有两个选择(a,b)
    那么我们考虑先选择哪一个
    (W_{a,b}=sum_{i=1}^{sz_1}(x+i) imes w_{1,i}+sum_{i=1}^{sz_2}(x+sz_1+i) imes w_{2,i}\ W_{b,a}=sum_{i=1}^{sz_2}(x+i) imes w_{2,i}+sum_{i=1}^{sz_1}(x+sz_2+i) imes w_{1,i}\ W_{a,b} > W_{b,a} o W_{2} imes sz_1 > W_{1} imes sz_2)

    所以我们可以按照(frac{W_i}{sz_i})从小到大来选择贪心
    用一个堆来维护
    考虑当前堆中权值最小的点(i)
    如果(i)没有父亲,那么直接选(i)
    否则就说明不能立即选择i
    那么先考虑ta的父亲
    因为ta的父亲之前也可能被一些更优的点依赖
    所以一旦选择了ta的父亲就会先处理那些更优的依赖
    然后处理完更优的依赖就立即来处理点(i)
    这个可以用并查集实现

    代码

    #include<queue>
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    # define LL long long
    const int M = 500005 ;
    using namespace std ;
    
    inline int read() {
        char c = getchar() ; int x = 0 , w = 1 ;
        while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
        while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
        return x*w ;
    }
    
    bool suc , vis[M] ;
    int n , f[M] , fa[M] ;
    LL ans , w[M] , sz[M] ;
    vector < int > vec[M] ; 
    struct Node { int u , sz ; LL w ; } ;
    inline bool operator < (Node a , Node b) {
        return a.sz * b.w < b.sz * a.w ;
    }
    int find(int x) {
        if(f[x] != x) f[x] = find(f[x]) ;
        return f[x] ;
    }
    priority_queue < Node > q ;
    void dfs(int u) {
        if(vis[u]) return void(suc = true) ; vis[u] = true ;
        for(int i = 0 , v ; i < vec[u].size() ; i ++) {
            v = vec[u][i] ;
            dfs(v) ;
        }
    }
    int main() {
        n = read() ;
        for(int i = 1 ; i <= n ; i ++) {
            fa[i] = read() ;
            vec[fa[i]].push_back(i) ;
        }
        for(int i = 1 ; i <= n ; i ++) w[i] = read() ;
        dfs(0) ;
        for(int i = 0 ; i <= n ; i ++)  
            if(!vis[i])	
                suc = true ;
        if(suc) { printf("-1
    ") ; return 0 ; }
        for(int i = 0 ; i <= n ; i ++)
            f[i] = i , sz[i] = 1 ;
        for(int i = 1 ; i <= n ; i ++)
            q.push((Node) { i , 1 , w[i] }) ;
        while(!q.empty()) {
            Node x = q.top() ; q.pop() ;
            int u = x.u , ff = find(fa[u]) ;
            if(find(u) == ff) continue ;
            f[u] = ff ;
            ans += 1LL * w[u] * sz[ff] ;
            sz[ff] += sz[u] ; w[ff] += w[u] ;
            if(ff)
                q.push((Node) { ff , sz[ff] , w[ff] }) ;
        }
        cout << ans << endl ;
        return 0 ;
    }
    
  • 相关阅读:
    LAMP动态网站安装脚本
    图片上传
    如何用qq代理发送邮件
    初识c#
    Eclipse使用技巧
    maven中 install的install:install的区别
    Git的各种状态
    phpStorm中Structure窗口中的符号代表的意思
    Apache+PHP+MySQL+phpMyAdmin+WordPress搭建
    Session重点整理
  • 原文地址:https://www.cnblogs.com/beretty/p/10763281.html
Copyright © 2020-2023  润新知