【5289】【HNOI2018】—排列(语文题)

传送门

看了20分钟硬是没看懂他在说什么
膜了题解之后
实际上就是一棵树,求一个代价最大的拓扑序
发现对于一个点选了之后会有一些更小的儿子必须选
即对于2个点i,ji,j
满足wisi+wj(si+sj)=>wi/si<wj/sjwi*si+wj*(si+sj)=>wi/si<wj/sj

用一个堆维护每一次找最小的一个就行了

#include<bits/stdc++.h>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
#define re register
#define ll long long
#define db long double
#define pii pair<db,int>
typedef __gnu_pbds::priority_queue <pii,greater<pii>,pairing_heap_tag> Heap;
typedef Heap::point_iterator it;

const int RLEN=1<<20|1;
inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}
inline int read() {
    char ch=nc(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}
const int N=500005;
int n,a[N],fa[N],f[N],w[N],dfn;
bool vis[N],o[N];
ll ans,sum[N],siz[N];
int adj[N],nxt[N<<1],to[N<<1],cnt,tot;
it pos[N];Heap q;
inline void addedge(int u,int v){
	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
}
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
bool dfs(int u){
	++dfn,vis[u]=true,fa[u]=u;
	for(int e=adj[u];e;e=nxt[e]){
		int v=to[e];
		if(vis[v])return false;
		f[v]=u;if(!dfs(v))return false;
	}
	return true;
}
int main(){
	n=read();
	for(re int i=1;i<=n;++i)a[i]=read(),addedge(a[i],i);
	for(re int i=1;i<=n;++i)w[i]=read(),sum[i]=w[i],siz[i]=1;siz[0]=1;
	if(!dfs(0)||dfn!=n+1){puts("-1");return 0;}
	for(re int i=0;i<=n;++i)pos[i]=q.push(pii((db)sum[i]/siz[i],i));
	while(!q.empty()){
		int u=q.top().second;q.pop();
		if(!u||o[find(f[u])]){
			ans+=tot*sum[u];
			tot+=siz[u];
			o[u]=1;
		}
		else{
			int f1=find(f[u]);
			ans+=siz[f1]*sum[u];
			siz[f1]+=siz[u];
			sum[f1]+=sum[u];
			fa[u]=f1;
			q.modify(pos[f1],pii((db)sum[f1]/siz[f1],f1));
		}
	}
	cout<<ans;
}
posted @ 2019-03-01 17:01  Stargazer_cykoi  阅读(149)  评论(0编辑  收藏  举报