题目链接:https://www.luogu.com.cn/problem/P3530
题目大意:
有 \(n\)
给你 \(m_1\) 对关系 \((a_i, b_i)\),表示第 \(a_i\) 个人的分数比第 \(b_i\) 个人的分数小 \(1\)。
在给你 \(m_2\) 对关系 \((c_i, d_i)\),表示第 \(c_i\) 个人的分数 \(\le\) 第 \(d_i\)
解题思路:
首先可以存在无解的情况,所以可以把问题转成差分约束求最短路,同时判负环的问题。
对于第一种关系 \((a, b)\),考虑 \(dis_a = dis_b - 1\),可以转成如下两个不等式:
- \(dis_a \le dis_b + (-1)\)(从 \(b\) 连向 \(a\) 一条长度为 \(-1\)
- \(dis_b \le dis_a + 1\)(从 \(a\) 连向 \(b\) 一条长度为 \(1\)
对于第二种关系 \((c, d)\),考虑 \(dis_c \le dis_d\),可以看成:
- \(dis_c \le dis_d + 0\)(从 \(d\) 连向 \(c\) 一条长度为 \(0\)
我们可以用 floyd 求最短路,如果 \(dis_{i, i} \lt 0\),说明存在负环。
在不存在负环的情况下考虑:
处于不同的强连通分量中的节点之间具体可以取哪些数没有是没有太大约束的。
但是同一个连通块内的节点取哪些数值是受到约束的。
考虑一个强连通分量内两点间的最长距离为 \(k\),则该强连通分量内最多存在的不同数值为 \(k + 1\)。
示例程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 606;
int n, m1, m2, dfn[maxn], low[maxn], idx, scc, id[maxn], dis[maxn][maxn], ans, val[maxn];
bool ins[maxn], vis[maxn];
stack<int> stk;
vector<int> g[maxn];
void tarjan(int u) {
dfn[u] = low[u] = ++idx;
stk.push(u);
ins[u] = true;
for (auto v : g[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (ins[v])
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
scc++;
int v;
do {
v = stk.top();
stk.pop();
ins[v] = false;
id[v] = scc;
} while (v != u);
}
}
int main() {
cin >> n >> m1 >> m2;
memset(dis, 0x3f, sizeof dis);
for (int i = 1; i <= n; i++) dis[i][i] = 0;
while (m1--) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
dis[a][b] = min(dis[a][b], 1);
dis[b][a] = min(dis[b][a], -1);
}
while (m2--) {
int c, d;
cin >> c >> d;
g[d].push_back(c);
dis[d][c] = min(dis[d][c], 0);
}
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
if (id[i] == id[k])
for (int j = 1; j <= n; j++)
if (id[i] == id[j])
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
for (int i = 1; i <= n; i++) {
if (dis[i][i] < 0) {
puts("NIE");
return 0;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (id[i] != id[j])
continue;
int tmp = abs(dis[i][j]);
val[ id[i] ] = max(val[ id[i] ], tmp);
}
}
for (int i = 1; i <= n; i++) {
int p = id[i];
if (!vis[p]) {
vis[p] = true;
ans += val[p] + 1;
}
}
cout << ans << endl;
return 0;
}