[CF444E]DZY Loves Planting

xiaoxiao2021-02-28  62

题目大意

给定一棵 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; }
转载请注明原文地址: https://www.6miu.com/read-54565.html

最新回复(0)