[CF444E]DZY Loves Planting

[CF444E]DZY Loves Planting

题目大意:

给出一个(n(nle10^5))个点的带边权的树。

定义(g(x,y))(x,y)两点路径上权值最大边的权值,并且如果(x=y)(g(x,y)=0)

对于一个长度为(n)的序列(P={p_1,p_2,ldots,p_n}(1le p_ile n)),定义(f(P)=minlimits_{i=1}^n g(i,p_i))

如果一个序列(P)是合法的,当且仅当元素(j)在序列(P)中出现的次数不超过(x_j)次。

求所有合法的序列(P)中,(f(P))的最大值。

思路:

将边权从小到大排序,依次合并每条边连接的两个连通块。

(sum=sumlimits_{i=1}^nx_i)

如果对于合并后的连通块(S)(sum-sum_{xin S}<|S|),则比当前边更大的边已经不能够让当前块内的点全部连出去了,已经不可能作为答案。答案即为当前边的权值。

源代码:

#include<cstdio>
#include<cctype>
#include<numeric>
#include<algorithm>
inline int getint() {
	register char ch;
	while(!isdigit(ch=getchar()));
	register int x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return x;
}
using int64=long long;
const int N=1e5+1;
int64 x[N];
struct DisjointSet {
	int anc[N],size[N];
	int find(const int &x) {
		return x==anc[x]?x:anc[x]=find(anc[x]);
	}
	void reset(const int &n) {
		std::iota(&anc[1],&anc[n]+1,1);
		std::fill(&size[1],&size[n]+1,1);
	}
	void merge(const int &u,const int &v) {
		const int p=find(u),q=find(v);
		anc[p]=q;
		x[q]+=x[p];
		size[q]+=size[p];
	}
};
DisjointSet s;
struct Edge {
	int u,v,w;
	bool operator < (const Edge &rhs) const {
		return w<rhs.w;
	}
};
Edge edge[N];
int main() {
	const int n=getint();
	for(register int i=1;i<n;i++) {
		const int u=getint(),v=getint();
		edge[i]=(Edge){u,v,getint()};
	}
	std::sort(&edge[1],&edge[n]);
	s.reset(n);
	int64 sum=0;
	for(register int i=1;i<=n;i++) {
		x[i]=getint();
		sum+=x[i];
	}
	for(register int i=1;i<n;i++) {
		const int &u=edge[i].u,&v=edge[i].v;
		s.merge(u,v);
		if(sum-x[s.find(u)]<s.size[s.find(u)]) {
			printf("%d
",edge[i].w);
			return 0;
		}
	}
	puts("0");
	return 0;
}
原文地址:https://www.cnblogs.com/skylee03/p/9871196.html