[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;
}