题意
传送门 AcWing 368
题解
将各类关系转化为差分约束系统,求解各节点亮度的最小值,将其转化为求解最长路。
设 d d d 代表节点的亮度。 T = 1 T=1 T=1 时, d [ A ] − d [ B ] ≥ 0 , d [ B ] − d [ A ] ≥ 0 d[A]-d[B]\geq 0,d[B]-d[A]\geq 0 d[A]−d[B]≥0,d[B]−d[A]≥0; T = 2 T=2 T=2 时, d [ B ] − d [ A ] ≥ 1 d[B]-d[A]\geq 1 d[B]−d[A]≥1; T = 3 T=3 T=3 时, d [ A ] − d [ B ] ≥ 0 d[A]-d[B]\geq 0 d[A]−d[B]≥0; T = 4 T=4 T=4 时, d [ A ] − d [ B ] ≥ 1 d[A]-d[B]\geq 1 d[A]−d[B]≥1; T = 5 T=5 T=5 时, d [ B ] − d [ A ] ≥ 0 d[B]-d[A]\geq 0 d[B]−d[A]≥0。为保证亮度最暗为 1 1 1,设 d [ 0 ] = 0 d[0]=0 d[0]=0,加上约束条件 ∀ A , d [ A ] − d [ 0 ] ≥ 1 \forall A,d[A]-d[0]\geq 1 ∀A,d[A]−d[0]≥1。
S P F A SPFA SPFA 求解最长路 O ( N M ) O(NM) O(NM),难以胜任。观察到差分约束系统中边权非负,若图中存在环,则环上边权和一定为 0 0 0;否则,将出现 x > x x>x x>x 这类无解情况。
T a r j a n Tarjan Tarjan 算法求解 S C C SCC SCC,判断所有强连通分量内部是否存在权值非零的边。若差分约束系统有解,则使用拓扑序 D P DP DP 求解最长路。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005, maxm = 3 * 100005;
int N, M, fg, num, low[maxn], dfn[maxn];
int top, st[maxn], nr, rec[maxn];
int scc, sc[maxn], deg[maxn], dp[maxn];
int tot, head[maxn], to[maxm], nxt[maxm], cost[maxm];
int tot_c, hc[maxn], tc[maxm], nc[maxm], cc[maxm];
bool vs[maxn], in[maxn];
inline void add(int x, int y, int z) {
to[++tot] = y, cost[tot] = z, nxt[tot] = head[x], head[x] = tot; }
inline void add_c(int x, int y, int z) {
tc[++tot_c] = y, cc[tot_c] = z, nc[tot_c] = hc[x], hc[x] = tot_c; }
void dfs(int x)
{
vs[x] = 1;
for (int i = head[x]; i; i = nxt[i])
{
int y = to[i], z = cost[i];
if (sc[y] != scc)
continue;
if (z)
fg = 0;
else if (!vs[y])
dfs(y);
}
}
void tarjan(int x)
{
low[x] = dfn[x] = ++num;
st[++top] = x, in[x] = 1;
for (int i = head[x]; i; i = nxt[i])
{
int y = to[i];
if (!dfn[y])
{
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if (in[y])
low[x] = min(low[x], dfn[y]);
}
if (low[x] == dfn[x])
{
++scc, nr = 0;
int y;
do
{
y = st[top--], in[y] = 0;
rec[++nr] = y, sc[y] = scc;
} while (y != x);
for (int i = 1; i <= nr; ++i)
vs[rec[i]] = 0;
dfs(x);
}
}
int main()
{
scanf("%d%d", &N, &M);
tot = tot_c = 1;
for (int i = 1, t, a, b; i <= M; ++i)
{
scanf("%d%d%d", &t, &a, &b);
if (t == 1)
add(b, a, 0), add(a, b, 0);
else if (t == 2)
add(a, b, 1);
else if (t == 3)
add(b, a, 0);
else if (t == 4)
add(b, a, 1);
else if (t == 5)
add(a, b, 0);
}
for (int i = 1; i <= N; ++i)
add(0, i, 1);
fg = 1;
for (int i = 0; i <= N; ++i)
if (!dfn[i])
tarjan(i);
if (!fg)
{
puts("-1");
return 0;
}
for (int i = 0; i <= N; ++i)
for (int j = head[i]; j; j = nxt[j])
{
int x = sc[i], y = sc[to[j]], z = cost[j];
if (x != y)
add_c(x, y, z), ++deg[y];
}
queue<int> q;
for (int i = 1; i <= scc; ++i)
if (!deg[i])
q.push(i);
while (q.size())
{
int x = q.front();
q.pop();
for (int i = hc[x]; i; i = nc[i])
{
int y = tc[i], z = cc[i];
dp[y] = max(dp[y], dp[x] + z);
if (!--deg[y])
q.push(y);
}
}
ll res = 0;
for (int i = 1; i <= N; ++i)
res += dp[sc[i]];
printf("%lld\n", res);
return 0;
}