[Luogu P3544] [BZOJ 2801] [POI2012]BEZ-Minimalist Security

48 篇文章 1 订阅
洛谷传送门
BZOJ传送门

题目描述

A map of Mafiatown’s road network is given.

The network consists of intersections and bidirectional streets that connect them.

The streets cross only at the intersections, but they may lead through tunnels or flyovers.

Each pair of intersections is linked by at most one street.

At every intersection v v v there is a police station manned by p(v)p(v) policemen.

A street linking the intersections u u u and v v v is considered safe if there are at least b ( u , v ) b(u,v) b(u,v) policemen in total in the two stations at the streets ends. Initially p ( u ) + p ( v ) ≥ b ( u , v ) p(u)+p(v)\ge b(u,v) p(u)+p(v)b(u,v) holds for every street.

However, due to an ongoing crisis the mayor Byteasar has ordained the Minimalist Security Act (MSA), which states that:

a certain number (which may be zero) of policemen is to be laid off from each police station (we denote the number of policemen laid off from the station at the intersection v v v by z ( v ) z(v) z(v)), after the layoff, the total number of the policemen at both ends of every street connecting some two intersections, say u u u and v v v, should equal b ( u , v ) b(u,v) b(u,v) exactly, i.e.:

p ( u ) − z ( u ) + p ( v ) − z ( v ) = b ( u , v ) p(u)-z(u)+p(v)-z(v)=b(u,v) p(u)z(u)+p(v)z(v)=b(u,v)

These rules do not determine uniquely how many policemen are to be laid off.

Byteasar wonders what is the minimum and the maximum number of laid off policemen (the sum of z z z values over all intersections) that complies with aforementioned rules.

一张 n n n个点 m m m条边的无向图,有点权有边权都是非负,且每条边的权值小于等于两个顶点的权值和,现在要将每个点减一个非负整数使得每条边权等于两个顶点的点权和,问最大修改代价和最小修改代价

输入输出格式

输入格式:

In the first line of the standard input there are two integers, n n n and m m m ( 1 ≤ n ≤ 500   000 1\le n\le 500\ 000 1n500 000, 0 ≤ m ≤ 3   000   000 0\le m\le 3\ 000\ 000 0m3 000 000), separated by a single space, that denote the number of intersections and the number of streets in Mafiatown, respectively.

The intersections are numbered from 1 1 1 to n n n.

In the second line n n n nonnegative integers separated by single spaces are given.

These are the numbers of policemen currently employed at successive stations, i.e., the values p ( 1 ) , p ( 2 ) , ⋯   , p ( n ) p(1),p(2),\cdots,p(n) p(1),p(2),,p(n) ( 0 ≤ p ( i ) ≤ 1 0 6 (0\le p(i)\le 10^6 (0p(i)106).

Each of the following mm lines describes a single bidirectional street. Such description consists of three integers, u i , v i , b ( u i , v i ) ( 1 ≤ u i , v i ≤ n , u i ≠ v i , 0 ≤ b ( u i , v i ) ≤ 1 0 6 ) u_i,v_i,b(u_i,v_i)(1\le u_i,v_i\le n,u_i\ne v_i,0\le b(u_i,v_i)\le 10^6) ui,vi,b(ui,vi)(1ui,vin,ui̸=vi,0b(ui,vi)106), separated by single spaces, that denote respectively: the numbers of the intersections at the ends of the street and the minimum total number of policemen that have to man the stations at those intersections.

输出格式:

If Byteasar’s ordinance can be carried out, your program should print, on the standard output, exactly one line with two integers separated by a single space.

The numbers should be the minimum and the maximum number of policemen that should be laid off in order to carry out the ordinance.

If carrying out the ordinance is impossible, your program should print a single line containing the word NIE (Polish for no).

输入输出样例

输入样例#1:
3 2
5 10 5
1 2 5
2 3 3
输出样例#1:
12 15

解题分析

其实是挺SB的一道题, 先转化为这样一个问题: 每个点有一个取值范围 [ 0 , p i ] [0,p_i] [0,pi] ,我们需要满足连接的边两端的和为 l e n i , j − p i − p j len_{i,j}-p_{i}-p_j leni,jpipj,求点权之和的最小值和最大值。

很显然如果我们确定一个点, 其他点都可以随之确定。

那么随便设一个点为 x x x, 其他点就可以表示为 ± x + b \pm x+b ±x+b b b b是一个常数。

这样我们就可以一遍 D F S DFS DFS搞定了。 如果原来 D F S DFS DFS到过这个点, 且 x x x的正负号不同, 我们就可以解出一个解。 否则如果常数项不一样, 那就显然无解。

注意还有几种无解的情况, 详见代码。

同时每个点还可以限制 x x x的大小, 解两个不等式就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define ll long long
#define gc nc() 
#define MX 500500
IN char nc()
{
    static const int buflen = 1e6;
    static char buf[buflen], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, buflen, stdin), p1 == p2) ? EOF : *p1++;
}
template <class T>
IN void in(T &x)
{
	static bool neg; static char c;
	x = 0; c = gc;
	for (; !isdigit(c); c = gc)
	if (c == '-') neg = true;
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
	if (neg) neg = false, x = -x;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int n, m, cnt;
struct INFO {ll c0; int c1;} dat[MX], res;
int head[MX], bel[MX], lim[MX];
bool vis[MX], deal[MX];
bool solved;
int sol;
ll ansd, ansu, up, down;
struct Edge {int to, nex, len;} edge[12005000];
IN void add(R int from, R int to, R int len)
{edge[++cnt] = {to, head[from], len}, head[from] = cnt;}
int find(R int now) {return bel[now] == now ? now : bel[now] = find(bel[now]);}
void DFS(R int now, R int fa)
{
	vis[now] = true; res.c0 += dat[now].c0, res.c1 += dat[now].c1;
	ll tar1 = -dat[now].c1, tar0, avai;
	if (dat[now].c1 > 0)
	{
		down = max(down, -dat[now].c0);
		up = min(up, lim[now] - dat[now].c0);
	}
	else
	{
		up = min(up, dat[now].c0);
		down = max(down, dat[now].c0 - lim[now]);
	}
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		if (edge[i].to == fa) continue;
		tar0 = edge[i].len - dat[now].c0;
		if (vis[edge[i].to])
		{
			if (tar1 == dat[edge[i].to].c1)
			{
				if (tar0 != dat[edge[i].to].c0) puts("NIE"), exit(0);
                //两个常数项不同, 无解。
			}
			else
			{
				avai = dat[edge[i].to].c0 - tar0;
				if (avai % 2 != 0) puts("NIE"), exit(0);
                //解出来是小数, 无解。
				avai = avai / (tar1 - dat[edge[i].to].c1);
				if (!solved)
				{
					solved = true;
					sol = avai;
				}
				else if (sol != avai) puts("NIE"), exit(0);
                //有两个解不同, 无解
			}
		}
		else
		{
			dat[edge[i].to] = {tar0, tar1};
			DFS(edge[i].to, now);
		}
	}
}
int main(void)
{
	int foo, bar, val; in(n), in(m);
	for (R int i = 1; i <= n; ++i) in(lim[i]), bel[i] = i;
	for (R int i = 1; i <= m; ++i)
	{
		in(foo), in(bar), in(val);
		val = lim[foo] + lim[bar] - val;
		bel[find(bar)] = find(foo);
		add(foo, bar, val), add(bar, foo, val);
	}
	for (R int i = 1; i <= n; ++i)
	{
		foo = find(i);
		if (!deal[foo])
		{
			solved = false; sol = 0;
			deal[foo] = true;
			dat[foo].c1 = 1, res = {0, 0};
			up = lim[foo], down = 0;
			DFS(foo, 0);
			if (solved)
			{
				if (sol >= down && sol <= up) 
				ansu += res.c1 * sol + res.c0, ansd += res.c1 * sol + res.c0;
				else return puts("NIE"), 0;//解出的解不在科学范围内, 无解。
			} 
			else
			{
				if (down > up) return puts("NIE"), 0;//上界比下界还小, 无解
				if (res.c1 >= 0) ansu += res.c1 * up + res.c0, ansd += res.c1 * down + res.c0;
				else ansu += res.c1 * down + res.c0, ansd += res.c1 * up + res.c0;
			}
		}
	}
	printf("%lld %lld", ansd, ansu);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值