zoukankan      html  css  js  c++  java
  • [bzoj5289][Hnoi2018]排列——贪心+堆

    题目大意:

    给定 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 imes w_j+(t+1) imes w_ileq t imes w_i+(t+1) imes w_j),即(w_ileq w_j),不断的交换,这样总会有更优的答案直到(i)紧接着(f)
    这样以后合并的点变成了一个有序的集合,对于集合内的贡献我们可以直接加入答案,最后的贡献取决于前面还有几个数。
    然后考虑对这些集合我们也选出里面"最小的",即选了它父亲之后一定要选它的,同样用上面的交换法可得:
    (t imes sum_j+(t+size_j) imes sum_ileq t imes sum_i+(t+size_i) imes sum_j),得(sum_i imes size_jleq sum_j imes 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
    ",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;
    }
    
    
  • 相关阅读:
    HDU 3829 Cat VS Dog (最大独立集)【二分图匹配】
    POJ 2594 Treasure Exploration (Floyd+最小路径覆盖)
    HDU 1054 Strategic Game (最小点覆盖)【二分图匹配】
    HDU 4185 Oil Skimming 【最大匹配】
    HDU 2389 Rain on your Parade 最大匹配(模板题)【HK算法】
    HDU 1281 棋盘游戏 (枚举+最大匹配)
    HDU 1045 Fire Net 【二分图匹配】
    POJ 1904 King's Quest (强连通分量+完美匹配)
    HDU 4635 Strongly connected (强连通分量+缩点)
    POJ 2631 Roads in the North (模板题)(树的直径)
  • 原文地址:https://www.cnblogs.com/ylsoi/p/10053940.html
Copyright © 2011-2022 走看看