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