码迷,mamicode.com
首页 > 其他好文 > 详细

[bzoj5289][Hnoi2018]排列——贪心+堆

时间:2018-12-02 16:46:06      阅读:230      评论:0      收藏:0      [点我收藏+]

标签:long   type   mes   get   namespace   air   its   dig   有一个   

题目大意:

给定 n 个整数 a 1 , a 2 , ..., a n ,0 ≤ a i ≤ n,以及 n 个整数 w 1 , w 2 , ..., w n 。称 a 1 , a 2 , ..., a n 的
一个排列 a p[1] , a p[2] , ..., a p[n] 为 a 1 , a 2 , ..., a n 的一个合法排列,当且仅当该排列满足:对于任意
的 k 和任意的 j,如果 j<=k,那么 a p[j] 不等于 p[k]。
(换句话说就是:对于任意的 k 和任意的j,如果 p[k]等于 a p[j] ,那么 k<j。)
定义这个合法排列的权值为 w p[1] + 2w p[2] + ... + nw p[n] 。你
需要求出在所有合法排列中的最大权值。如果不存在合法排列,输出-1。
样例解释中给出了合法排列和非法排列的实例。

思路:

首先我们简单分析一下可以得到最后的模型是一片森林然后按照拓扑序进行选点,求最后的答案最大。
根据排序不等式,首先想到的是直接选择目前可以选择的点里面最小的,然后依次进行。但是这样会有问题,因为我们可以选择一个较大的来解锁很多更小的来达到更优的答案。
于是我们先考虑对于全局最小的点\(w_i\),如果它的父亲被选了,那么紧接着一定要选它,这样可以保证最优,简单地证明:
假设父亲\(f\)被选了之后\(i\)并不是紧接着父亲,那么我们在序列上把\(i\)和i前面\(j\)的进行交换,可以得到\(t\times w_j+(t+1)\times w_i\leq t\times w_i+(t+1)\times w_j\),即\(w_i\leq w_j\),不断的交换,这样总会有更优的答案直到\(i\)紧接着\(f\)
这样以后合并的点变成了一个有序的集合,对于集合内的贡献我们可以直接加入答案,最后的贡献取决于前面还有几个数。
然后考虑对这些集合我们也选出里面"最小的",即选了它父亲之后一定要选它的,同样用上面的交换法可得:
\(t\times sum_j+(t+size_j)\times sum_i\leq t\times sum_i+(t+size_i)\times sum_j\),得\(sum_i\times size_j\leq sum_j\times size_i\),即比较两个集合的平均数即可。
这样不断地找到最小的合并,直到只有一个点为止。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i])
#define debug(x) cout<<#x<<"="<<x<<endl
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj5289.in","r",stdin);
    freopen("bzoj5289.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=5e5+10;
int n,a[maxn],fa[maxn];
ll w[maxn],ans;

struct union_set{
    int fa[maxn];
    void setall(){REP(i,1,n)fa[i]=i;}
    int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}
    void merge(int x,int y){fa[find(y)]=find(x);}
}T;

struct node{
    int id,sz;
    ll sum;
    bool operator < (const node & tt) const {
        return sum*tt.sz>tt.sum*sz;
    }
}b[maxn];
priority_queue<node>h;

bool gg(){
    T.setall();
    REP(i,1,n)if(a[i]){
        if(T.find(a[i])==T.find(i))return true;
        T.merge(a[i],i);
        fa[i]=a[i];
    }
    return false;
}

void work(){
    T.setall();
    REP(i,1,n){
        b[i]=(node){i,1,w[i]};
        ans+=w[i];
        h.push(b[i]);
    }
    while(!h.empty()){
        node t=h.top(); h.pop();
        if(b[t.id].sz!=t.sz)continue;
        int f=T.find(fa[t.id]);
        T.merge(f,t.id);
        ans+=b[f].sz*t.sum;
        b[f].sz+=t.sz;
        b[f].sum+=t.sum;
        if(f)h.push(b[f]);
    }
    printf("%lld\n",ans);
}

int main(){
    File();
    read(n);
    REP(i,1,n)read(a[i]);
    REP(i,1,n)read(w[i]);
    if(gg())return puts("-1"),0;
    work();
    return 0;
}

[bzoj5289][Hnoi2018]排列——贪心+堆

标签:long   type   mes   get   namespace   air   its   dig   有一个   

原文地址:https://www.cnblogs.com/ylsoi/p/10053940.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!