传送门:洛谷 P1993
题目描述
小 K K K在 M C MC MC里面建立很多很多的农场,总共 n n n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共 m m m个),以下列三种形式描述:
- 农场 a a a比农场 b b b至少多种植了 c c c个单位的作物,
- 农场 a a a比农场 b b b至多多种植了 c c c个单位的作物,
- 农场 a a a与农场 b b b种植的作物数一样多。
但是,由于小 K K K的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。
分析
本题仅分为两部分,建图+判负环
- 建图 — 差分约束
在一张的图上边 ( u , v , w ) (u, v, w) (u,v,w),必定满足不等式 d i s [ u ] + w ≥ d i s [ v ] dis[u] + w \geq dis[v] dis[u]+w≥dis[v],对于本题而言,也有类似的关系:
令 f [ x ] f[x] f[x]表示农场 x x x的作物数量,则有
- f [ a ] − f [ b ] ≥ c    ⟺    f [ a ] − c ≥ f [ b ] f[a] - f[b] \geq c \iff f[a] - c \geq f[b] f[a]−f[b]≥c⟺f[a]−c≥f[b]
- f [ a ] − f [ b ] ≤ c    ⟺    f [ b ] + c ≥ f [ a ] f[a] - f[b] \leq c \iff f[b] + c \geq f[a] f[a]−f[b]≤c⟺f[b]+c≥f[a]
-
f
[
a
]
=
f
[
b
]
  
⟺
  
f
[
a
]
≥
f
[
b
]
&
f
[
b
]
≥
f
[
a
]
f[a] = f[b] \iff f[a] \geq f[b] \quad \& \quad f[b] \geq f[a]
f[a]=f[b]⟺f[a]≥f[b]&f[b]≥f[a]
那么类似的,将 a a a、 b b b看做节点, c c c、 − c -c −c看做边权建图即可
- 判负环 —
d
f
s
dfs
dfs版
s
p
f
a
spfa
spfa
由于题目只要求是否可行,那么就只要判负数就可以了。
d f s dfs dfs版的判断依据:第二次访问该节点即存在负环
优化: d i s dis dis数组可以不用赋初值,直接为 0 0 0即可。可以减少没必要的迭代。
注意:图不一定联通,要 f o r for for一遍(不判实测 70 70 70)
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define IL inline
using namespace std;
IL int read()
{
char c = getchar();
int sum = 0 ,k = 1;
for(;'0' > c || c > '9'; c = getchar())
if(c == '-') k = -1;
for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
return sum * k;
}
int n, m;
int to[20005], nxt[20005], val[20005];
int cnt, last[10005];
IL void add(int u, int v, int w)
{
//printf("%d %d %d\n", u, v, w);
to[++cnt] = v; nxt[cnt] = last[u]; val[cnt] = w; last[u] = cnt;
}
bool vis[10005];
int dis[10005];
IL bool dfs_spfa(int u)
{
vis[u] = 1;
for(int i = last[u], v; (v = to[i]); i = nxt[i])
if(dis[u] + val[i] < dis[v])
{
dis[v] = dis[u] + val[i];
if(vis[v] || dfs_spfa(v)) return 1;
}
vis[u] = 0;
return 0;
}
int main()
{
n = read(); m = read();
for(int k, x, y, z; m; --m)
{
k = read(); x = read(); y = read();
if(k != 3) z = read();
if(k == 1) add(x, y, -z); else
if(k == 2) add(y, x, z); else
if(k == 3) { add(x, y, 0); add(y, x, 0); }
}
int flag;
for(int i = 1; i <= n; ++i)
if(!vis[i])
{
flag = dfs_spfa(i);
if(flag) break;
}
printf("%s\n", flag ? "No" : "Yes" );
return 0;
}