差分约束系统
我们可以把一个不等式,看做两个点和一条边,而边则是约束不等式的一个条件
距离 a - b >= c
那么 建边就是 a---c---b ,a b之间建立一条权值为 c 的边,然后我们还可以根据不等式的运算进行一部分约束
对于多个不等式组:
v(x1) - v(x2) >= c1
v(x2) - v(x3) >= c2
……
把它们加起来就会得到:v(x1) - v(xn) >= c1 + c2 + …… + cn-1
而对于所有的不等式,倘若我们满足了 >= 中的最大值,那么所有条件不也就都满足了吗
所以求v(x)-v(y)的最小值是在图上求最长路径。(摘自题解上某个巨巨的博客)
而题目中判断是否成立,也就是看一下这个差分约束系统是否存在一个矛盾,
首先所有数字大于0, 我们不妨建立一个 V(x) - 0 >= 0 把所有点和 0 连一条边就好了
只要求出所有不等式中的最小值,代表着这个系统可以被满足
但是如果光这么看,我们发现这个图就是一棵树。。没有完全连通,那么我们便在最后加一个辅助点,使得整个图变成连通的,
之后我们需要做的就是找一下整个图中的负环,因为一旦存在负环,说明这个系统内部出了问题,出现矛盾了,就不成立了,那么我们反向思路,只要找到一个正环,这道题不就有可成立的情况了吗。。
所以我们用 spfa 来跑一次求负环。。但是这道题被 卡了 tle 好几组数据。。最后看了下题解。。发现竟然 dfs 优化。。
有点玄学。。但是这道题。。非常不错。。
以下 是 AC 代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
#define pb push_back
inline int read()
{
int s=0,w=1;char ch = getchar();
while(ch<'0'||ch>'9'){if(ch == '-')w=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){s = s*10 + ch-'0';ch=getchar();}
return w*s;
}
struct node
{
int to,val,nxt;
}ed[maxn];
int head[maxn],tot;
inline void add(int a, int b, int v)
{
ed[++tot].to = b;
ed[tot].val = v;
ed[tot].nxt = head[a];
head[a] = tot;
}
int dis[maxn];
bool vis[maxn];
bool inque[maxn];
bool spfa(int s)
{
inque[s] = true;
for(int i=head[s];~i;i=ed[i].nxt)
{
int to = ed[i].to;
int val = ed[i].val;
if(!vis[to] || dis[to] < dis[s] + val)
{
if(inque[to])
return false;
vis[to] = true;
dis[to] = dis[s] + val;
if(!spfa(to))return false;
}
}
inque[s] = false;
return true;
}
int main()
{
int n=read(),m=read();
memset(head,-1,sizeof head);
tot = 0;
for(int i=1;i<=n;i++)
{
add(i, 0, 0);
add(n+1, i, 0);
}
for(int i=1;i<=m;i++)
{
int t=read();
if(t == 1)
{
int a=read(),b=read(),c=read();
add(a, b, c);
}
if(t == 2)
{
int a=read(),b=read(),c=read();
add(b, a, -c);
}
if(t == 3)
{
int a=read(),b=read();
add(a, b, 0);
add(b, a, 0);
}
}
if(spfa(n+1))
puts("Yes");
else
puts("No");
return 0;
}