[CF444E]DZY Loves Planting

[CF444E]DZY Loves Planting

题目大意:

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

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

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

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

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

思路:

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

\(sum=\sum\limits_{i=1}^nx_i\)

如果对于合并后的连通块\(S\)\(sum-\sum_{x\in 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\n",edge[i].w);
			return 0;
		}
	}
	puts("0");
	return 0;
}
posted @ 2018-10-29 16:07  skylee03  阅读(198)  评论(0编辑  收藏  举报