JZOJ5916. 【NOIP2018模拟10.20】flow

Description

你是申国的一个地方长官,你手下有n个城市。
为了加强基础设施建设,在2020全面建成小康社会,统筹推进经济建设、政治建设、文化建设、社会建设、生态文明建设,坚定实施科教兴国战略、人才强国战略、创新驱动发展战略、乡村振兴战略、区域协调发展战略、可持续发展战略、军民融合发展战略,突出抓重点、补短板、强弱项,特别是要坚决打好防范化解重大风险、精准脱贫、污染防治的攻坚战,使全面建成小康社会得到人民认可、经得起历史检验。你认为本省的水利调配非常有问题,这导致部分地区出现严重的缺水,而部分地区却全年洪灾泛滥。
于是你打算将原有的但是已经废弃了的m条水管重新使用。第i条水管连接城市xi和yi。这些水管联通了所有城市。每座城市对水的需求不同设为ai,部分城市处于缺水状态,ai为正,缺水量刚好为ai mol。部分城市因为有水库,ai为负,它需要向外输送-ai mol的水才能不形成洪灾。对于每条水管,你需要决定它的输送量fi,若fi为正则表示从xi向yi输送fi mol的水,fi为负则表示从yi向xi输送-fi mol的水。
你需要做到每个城市都刚好满足它的需求,即缺ai mol水的城市需要刚好输入ai的水,而多出-ai mol水的城市需要刚好输出-ai mol水。
你需要判断能否满足要求,若满足,你还需要输出所有的f。

题解

判断是否合法很简单,
就是看看a的和是否为0。
现在考虑如何构造出一种合法解。
一种很简单的方法就是先构造一棵生成树,
强制只用树上面的边,这样就可以轻松求出一组合法解。

code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 300003
#define M 103
#define db double
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
	n=0;
	ch=G();
	while((ch<'0' || ch>'9') && ch!='-')ch=G();
	int w=1;
	if(ch=='-')w=-1,ch=G();
	while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
	n*=w;
}

void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

int n,m,x,y,f[N],nxt[N*2],to[N*2],id[N*2],lst[N],tot,a[N];
ll s,v[N],ans[N];

int get(int x){return f[x]=(f[x]==x?x:get(f[x]));}

void ins(int x,int y,int z)
{
	nxt[++tot]=lst[x];
	to[tot]=y;
	id[tot]=z;
	lst[x]=tot;
}

void dfs(int x,int fa)
{
	v[x]=a[x];
	for(int i=lst[x];i;i=nxt[i])
		if(to[i]^fa)
		{
			dfs(to[i],x);
			v[x]=v[x]+v[to[i]];
			if(i&1)ans[id[i]]=v[to[i]];else ans[id[i]]=-v[to[i]];
		}
}

int main()
{
	freopen("flow.in","r",stdin);
	freopen("flow.out","w",stdout);
	
	read(n);
	for(int i=1;i<=n;i++)
		read(a[i]),f[i]=i,s=s+a[i];
	if(s)
	{
		puts("Impossible");
		return 0;
	}
	puts("Possible");
	read(m);tot=0;
	for(int i=1;i<=m;i++)
	{
		read(x);read(y);
		if(get(x)^get(y))
		{
			ins(x,y,i);
			ins(y,x,i);
			f[get(x)]=get(y);
		}
	}
	dfs(1,0);
	for(int i=1;i<=m;i++)
	{
		if(ans[i]<0)P('-'),ans[i]=-ans[i];
		write(ans[i]);P('\n');
	}
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值