uva11478 Halum

微微发光的传送门

题目中给了一种操作,就是制定一个d,和一个结点v,把所有以v为终点的边的权值减少d,把所有以v为起点的边权增加d,然后使最小的边权最大。

看到最小最大这类的字眼,首先想到的就是二分答案,假设每一条边的边权都不小于x,对于一条边,是从a指向b的,然后假定sum(a)和sum(b)分别是左右于a和b上的所有操作的d的和,那么对于这条边,它的权重就应该是w+sum(a)-sum(b)>=x,整理一下就可以得到sum(b)-sum(a)<=w-x,这样就得到了一个差分约束系统了,如果它是有解的,那么二分到的x就是合理的,如果无解,就只能说x大了。

有两种情况需要特判,如果x是最大边权,差分约束系统依然有解,就说明最小边权可以任意大,如果x是1,差分约束系统就无解了,就说明找不到一个最小最大非负边权。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 500 + 10;
const int maxm = 2700 + 10;
const int INF = 0x7fffffff;
struct Edge{
	int to, dist;
};
struct SPFA{
	int n, m;
	Edge edges[maxm];
	int head[maxn], next[maxn];
	bool inq[maxn];
	int d[maxn], cnt[maxn];
	void init(int n){
		this -> n = n;
		m = 0;
		memset(head, -1, sizeof(head));
	}
	void AddEdge(int from, int to, int dist){
		next[m] = head[from];
		head[from] = m;
		edges[m++] = (Edge){to, dist};
	}
	bool spfa(){
		queue<int> que;
		memset(inq, 0, sizeof(inq));
		memset(cnt, 0, sizeof(cnt));
		for (int i = 0; i < n; i++){
			d[i] = 0; que.push(i);
		}
		while(!que.empty()){
			int u = que.front(); que.pop();
			inq[u] = 0;
			for (int i = head[u]; i != -1; i = next[i]){
				Edge& e = edges[i];
				if (d[e.to] > d[u] + e.dist){
					d[e.to] = d[u] + e.dist;
					if (!inq[e.to]){
						inq[e.to] = 1; que.push(e.to);
						if (++cnt[e.to] > n) return 1;
					}
				}
			}
		}
		return 0;
	}
};
SPFA solver;
bool test(int x){
	for (int i = 0; i < solver.m; i++)
		solver.edges[i].dist -= x;
	bool ret = solver.spfa();
	for (int i = 0; i < solver.m; i++)
		solver.edges[i].dist += x;
	return !ret;
}
int main(){
//	freopen("in.txt", "r", stdin);
	int n, m;
	while(~scanf("%d%d", &n, &m)){
		solver.init(n);
		int ub = 0;
		int x, y, z;
		while(m--){
			scanf("%d%d%d", &x, &y, &z);
			ub = max(z, ub);
			x -= 1; y -= 1;
			solver.AddEdge(x, y, z);
		}
		if (test(ub + 1)) puts("Infinite");
		else if (!test(1)) puts("No solution");
		else{
			int L = 2, R = ub, ans = 1;
			while(L <= R){
				int M = L + ((R - L) >> 1);
				if (test(M)){ans = M; L = M + 1;}
				else R = M - 1;
			}
			printf("%d\n", ans);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值