【洛谷P5290】【十二省联考2019】春节十二响(贪心+启发式合并)

传送门

首先可以显然的发现段数一定是最长链的长度
手玩一下样例
似乎可以贪心,每次把最大的一个个丢?
事实证明这是对的
这样就有60pts60pts
再把链写了就可以拿到75pts75pts的好成绩

考虑100分,如果合并2条子树的链
由排序不等式可以证明第2边第ii大的数放一起是最有的
那就可以利用优先队列启发式合并做

看起来复杂度是O(nlog2n)O(nlog^2n)
但实际上考虑我们只会将小的那一个堆和大的比较,而且只取值大的
实际上每个点只会被poppop一次

所以复杂度其实是O(nlogn)O(nlogn)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int RLEN=1<<20|1;
inline char gc(){
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob)?EOF:*ib++;
}
inline int read(){
	char ch=gc();
	int res=0,f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return res*f;
}
const int N=200005;
int siz[N],rt[N],val[N],n;
int adj[N],nxt[N],to[N],cnt,fa[N],stk[N],top;
ll ans;
priority_queue<int> q[N];
inline void addedge(int u,int v){
	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
}
void dfs(int u){
	for(int e=adj[u];e;e=nxt[e]){
		int v=to[e];
		dfs(v);
		if(q[rt[v]].size()>q[rt[u]].size())swap(rt[u],rt[v]);
		while(q[rt[v]].size())stk[++top]=max(q[rt[u]].top(),q[rt[v]].top()),q[rt[u]].pop(),q[rt[v]].pop();
		while(top)q[rt[u]].push(stk[top--]);
	}
	q[rt[u]].push(val[u]);
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)rt[i]=i,val[i]=read();
	for(int i=2;i<=n;i++){
		fa[i]=read(),addedge(fa[i],i);
	}
	dfs(1);
	while(q[rt[1]].size())ans+=q[rt[1]].top(),q[rt[1]].pop();
	cout<<ans;
}