洛谷多校补题 图

Voluntary Hotel

题目链接

题意:

有点乱。。。
有n(1e4)个点。 共有m(1e4)条无向边边。有边权,边权是两个点的距离(1e9),其中有p(100)个点是医院,第 i 个点在点 yi[i] 处,q个宾馆,第 i 个馆在点 y[i] 处,可以最多容纳 h[i] 个人,有 r 个医生,家在s[i]的点,要去第k[i]个医院上班,k[i] >=1 && k[i] <= p; k[i]是下标也就是在点yi[k[i]]处上班。医生可以住宾馆,然后医生去上班的最远距离是d,问d最小是多少。。。
好绕。。。

题解:

首先找最大的最小值这种基本都是二分的吧,就考虑怎么check,
如果家到他去医院的距离小于等于x的话就不用住酒店了。
剩下的人就是在宾馆住,去同一个医院的可以一视同仁,然后就是给这些医生安排酒店了,,怎么安排? 我想不到的,,
网络流 建立源点到宾馆的边,边权为宾馆的最多容纳人数,宾馆到医院的边,边权为inf,医院到汇点的边,边权为剩下的那些医生要去每个医院的人数,宾馆到医院的边中必须满足这个医院到这个宾馆的距离小于等于x。
然后跑一个最大流,如果可以最大流等于剩下的人数,那么就符合,否则不符合,
于是就可以先预处理一个每个医院为源点到其他地方的最短路。医院有100个,点有1e4个。所以不会超时
然后直接二分就可以。
代码就是比较头疼的了。。这么长的代码,害怕wa,找bug找死了。。

代码:
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
typedef pair<int,ll> pii;
const int maxn = 1e4+5;
const ll inf = 0x3f3f3f3f3f3f3f3f;
struct Edge
{
	int nex,id;
	ll val;
}edge[maxn<<2];
int head[maxn];
int cnt;
void add(int x,int y,ll val)
{
	edge[cnt].val = val;
	edge[cnt].id = y;
	edge[cnt].nex = head[x];
	head[x] = cnt++ ;
}
int n,m,p,q,r;
int yi[maxn];
ll dis[105][10005];
int vis[maxn];
struct cmp
{
	bool operator()(const pii & a, const pii & b){
		return a.second > b.second;
	}
};
priority_queue<pii,vector<pii>,cmp>qq;
void djs(int s,int k)
{
	for (int i = 1; i <= n; i ++ )
	{
		dis[k][i] = inf;
		vis[i] = 0;
	}
	dis[k][s] = 0;
	qq.push(make_pair(s,0));
	while(!qq.empty())
	{
		pii t = qq.top();
		qq.pop();
		int v=  t.first;
		if(vis[v])
			continue;
		vis[v] = 1;
		for (int i = head[v]; ~i ; i = edge[i].nex)
		{
			int p = edge[i].id;
			if(dis[k][p] > dis[k][v] + edge[i].val)
			{
				dis[k][p] = dis[k][v] + edge[i].val;
				qq.push(make_pair(p,dis[k][p]));
			}
		}
	}
}
const int M = 105;
int y[M]; //宾馆标号
int h[M]; //最多住的人
int s[1000006]; //每个医生的家
int k[1000006]; //每个医生要去的医院
int head2[M<<4];
Edge edge2[maxn<<2];
int cnt2;
void add2(int x,int y,int val)
{
	edge2[cnt2].id = y;
	edge2[cnt2].val = val;
	edge2[cnt2].nex = head2[x];
	head2[x] = cnt2 ++ ;
}
int st,et;
queue<int> qt;
int dep[M << 3];
bool bfs()
{

	for (int i = 1; i <= et; i ++ )
		dep[i] = 0;
	qt.push(st);
	dep[st] = 1;
	while(!qt.empty())
	{
		int x = qt.front();
		qt.pop();
		 // printf("%d\n",x);
		for (int i = head2[x]; ~i; i = edge2[i].nex)
		{
			// printf("%d\n", i);;
			int v = edge2[i].id;
			if(dep[v] == 0 && edge2[i].val > 0)
			{
				qt.push(v);
				dep[v] = dep[x] + 1;
			}
		}
	}
	return dep[et] != 0;
}

int dfs(int x,int flow)
{
	// printf("%d\n",x);
	if(x == et)
		return flow;
	int sum = 0;
	for (int i = head2[x]; ~i;  i = edge2[i].nex)
	{
		int v = edge2[i].id;
		if(dep[v] == dep[x] + 1 && edge2[i].val > 0)
		{
			int s = dfs(v,min(edge2[i].val,1ll * flow));
			
			edge2[i].val -= s;
			edge2[i ^ 1].val += s;
			sum += s;
			flow -= s;
		}
	}
	if(sum == 0)
		dep[x] = 0;
	return sum;
}

int sum[M];
bool pd(ll x)
{
	memset(head2,-1,sizeof(head2));
	memset(sum,0,sizeof(sum));
	cnt2 = 0;
	for (int i = 1; i <= r; i++ )
	{
		if(dis[k[i]][s[i]] <= x)
			continue;
		sum[k[i]] ++ ;
	}

	int sn = 0;
	for (int i = 1; i <= p; i ++ )
	{
		sn += sum[i];
	}
	int ss = p + q;
	st = ss + 1, et = ss + 2;
	for (int i = 1; i <= p; i ++ )
	{
		add2(st,i,sum[i]);
		add2(i,st,0);
	}
	for (int i = 1; i <= p; i ++ )
	{
		for (int j = 1; j <= q; j ++ )
		{
			if(dis[i][y[j]] <= x)
			{
				add2(i,j + p,10000007);
				add2(j + p,i,0);
			}
		}
	}
	for (int i = 1; i <= q; i ++ )
	{
		add2(et,i + p,0);
		add2(i + p,et,h[i]);
	}
	int ans =0 ;
	 
	while (1)
	{
		if(!bfs())
			break;
		ans += dfs(st,10000000);
	}
	// printf("%lld %d %d\n",x,ans,sn);
	return ans == sn;

}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d%d%d%d",&n,&m,&p,&q,&r);
	cnt = 0;
	for(int i= 1; i <= m; i ++ )
	{
		int x,y,val;
		scanf("%d%d%d",&x,&y,&val);
		add(x,y,val);
		add(y,x,val);
	}
	for (int i = 1; i <= p; i ++ )
		scanf("%d",&yi[i]);
	for (int i = 1; i <= p; i ++ )
		djs(yi[i],i);
	// for (int i =1; i <= p; i ++ )
	// {
	// 	for (int j = 1; j <= n; j ++ )
	// 	{
	// 		printf("%lld ",dis[i][j]);
	// 	}
	// 	printf("\n");
	// }
	for (int i = 1; i <= q; i ++ )
		scanf("%d%d",&y[i],&h[i]);
	for (int i = 1; i <= r; i ++ )
		scanf("%d%d",&s[i],&k[i]);
	ll l = 0, rr = ll(1e14);
	ll ans = ll(1e14);
	while(l <= rr)
	{
		ll mid = l + rr >> 1;
		if(pd(mid))
		{
			ans = mid;
			rr = mid - 1;
		}
		else
			l = mid + 1;
	}
	printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值