SP287

前言

我们可以发现这道题一眼就是二分了,但是唯一的问题就是如何写 check 函数。

思路

这里我们可以看到 标签 中有 网络流 那么我们就来想一下如何写,这里我们可以将 S 与所有的特殊点都连上一条流量为 \(1\) 的边,然后由于我们传进来的一个数为 \(x\) 然后在将所有正常的边都变成流量为 \(x\) 的边,这里即可代表每一条边的最多能走 \(x\) 次。

然后我们直接跑一个正常的网络流即可,然后我们就判断 \(maxflow\)\(k\) 的关系即可,若大于等于就满足条件。

代码

#include <bits/stdc++.h>
using namespace std ;
#define int long long
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define rep1(i,x,y) for(int i=x;i>=y;i--)
#define fire signed
#define kong putchar(' ')
#define end putchar('\n')
#define in(x) scanf("%lld",&x)
#define lcm(x,y) x*y/__gcd(x,y)
#define il inline
il void print(int x) {
	if(x>=10) print(x/10);
	putchar(x%10+'0');
}
int tot=1;
const int N=1e5+10;
int head[N],n,m,k;
struct node {
	int x,y,z;
} edg[N];
int d[N],s;
int a[N],t;
void add(int x,int y,int z) {
	edg[++tot]= {y,head[x],z};
	head[x]=tot;
	if(x!=s) edg[++tot]= {x,head[y],z};
	else edg[++tot]={x,head[y],0};
	head[y]=tot;
}
int x[N],y[N];
int bfs() {
	queue<int>q;
	q.push(s);
	memset(d,0,sizeof d);
	d[s]=1;
	while(q.size()) {
		int x=q.front();
		q.pop();
		for(int i=head[x]; ~i; i=edg[i].y) {
			int to=edg[i].x;
			if(!d[to]&&edg[i].z) {
				d[to]=d[x]+1;
				q.push(to);
				if(to==t) return 1;
			}
		}
	}
	return false;
}
int dinic(int x,int flow) {
	if(x==t) return flow;
	int pl=false;
	for(int i=head[x]; ~i&&flow; i=edg[i].y) {
		int to=edg[i].x;
		if(d[to]==d[x]+1&&edg[i].z) {
			int k=dinic(to,min(edg[i].z,flow));
			if(!k) d[to]=-1;
			edg[i].z-=k;
			pl+=k;
			edg[i^1].z+=k;
			flow-=k;
		}
	}
	return pl;
}
bool check(int ff) {
	memset(head,-1,sizeof head);
	tot=1;
	t=1;
	s=n+1;
	rep(i,1,k) add(s,a[i],1);
	rep(i,1,m) add(x[i],y[i],ff);
	int flow=0;
	while(bfs()) flow+=dinic(s,INT_MAX);
	return flow>=k;
}
fire main() {
	int T;
	cin>>T;
	while(T--) {
		in(n),in(m),in(k);
		rep(i,1,k) in(a[i]);
		rep(i,1,m) cin>>x[i]>>y[i];
		int l=0,r=k+1,ans=0;
		while(l<=r) {
			int mid=l+r>>1;
			if(check(mid)) r=mid-1,ans=mid;
			else l=mid+1;
		}
		print(ans);
		end;
	}
	return false;
}

posted @ 2024-01-31 11:17  highkj  阅读(6)  评论(0)    收藏  举报