[CF444E]DZY Loves Planting

来源:互联网 发布:mac如何隐藏菜单栏 编辑:程序博客网 时间:2024/06/05 04:33

题目大意

给定一棵n个点的树,每条边有边权ci
定义g(x,y)表示点x到点y路径上边权的最大值。特殊地g(x,x)=0
对于一个序列{pn}(1pin),定义

f(p)=mini=1ng(i,pi)

现在有n个限制xi,表示i{pn}中的出现的次数不超过xi

1ci104,1xin
原数据范围n3000


题目分析

首先有个很显然的想法:二分答案,然后用网络流来判定答案是否可行。于是乎好像可以过掉原数据???
来考虑更优的做法:可以发现网络流的过程其实就是一个二分图匹配,考虑怎么把这个问题套上Hall定理。
从小到大加边,然后我们可以把联通块缩起来,这个联通块内所有点都要向联通块外的点匹配才能获得当前边权的答案。根据Hall定理其实就是联通块内点数小于等于联通块外所有点x的和才合法。
直接使用并查集搞一搞就可以了。
时间复杂度O(nα(n))


代码实现

#include <algorithm>#include <iostream>#include <cstdio>#include <cctype>using namespace std;int read(){    int x=0,f=1;    char ch=getchar();    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();    return x*f;}const int N=3005;struct edge{    int x,y,len;    bool operator<(edge const e){return len<e.len;}}ed[N];int fa[N],rk[N],a[N],size[N];int n,ans,err,sum;int getfather(int son){return fa[son]==son?son:fa[son]=getfather(fa[son]);}void merge(int x,int y){    x=getfather(x),y=getfather(y);    if (x==y) return;    if (rk[x]<rk[y]) swap(x,y);    fa[y]=x,rk[x]+=rk[x]==rk[y];    err-=sum-a[x]<size[x],err-=sum-a[y]<size[y];    size[x]+=size[y],a[x]+=a[y],err+=sum-a[x]<size[x];}void calc(){    err=0;    for (int i=1;i<=n;++i) err+=sum-a[i]<(size[i]=1),fa[i]=i,rk[i]=0;    for (int cur=1;cur<n;)    {        if (!err) ans=ed[cur].len;        for (int ptr=cur;cur<n&&ed[cur].len==ed[ptr].len;++cur) merge(ed[cur].x,ed[cur].y);    }}int main(){    freopen("planting.in","r",stdin),freopen("planting.out","w",stdout);    n=read();    for (int i=1;i<n;++i) ed[i].x=read(),ed[i].y=read(),ed[i].len=read();    sort(ed+1,ed+n);    for (int i=1;i<=n;++i) sum+=a[i]=read();    calc(),printf("%d\n",ans);    fclose(stdin),fclose(stdout);    return 0;}