ABC277Ex - Constrained Sums 题解 2-SAT
题目链接:https://atcoder.jp/contests/abc277/tasks/abc277_h
用:
- \(t(i,j,0)\) 表示 \(X_i \ge j\) 这个状态
- \(t(i,j,1)\) 表示 \(X_i \lt j\) 这个状态
首先,对于任意 \(1 \le i \le n\):
- 能保证 \(X_i \ge 0\)
- \(t(i,0,1) \to t(i,0,0)\)
- 能保证 \(X_i \lt m+1\)(即 \(X_i \le m\))
- \(t(i,m+1,0) \to t(i,m+1,1)\)
对于任意 \(1 \le i \le n, 1 \le j \le m+1\):
- 如果 \(i \ge j\),则肯定满足 \(i \ge j-1\)
- \(t(i,j,0) \to t(i,j-1,0)\)
- 如果 \(i \lt j-1\),则肯定满足 \(i \lt j\)
- \(t(i,j-1,1) \to t(i,j,1)\)
对于每组 \(a, b, L, R\):
对于 \(X_a + X_b \ge L\):
对于 \(0 \le i \le m+1\),
- 如果 \(X_a \lt i\),则 \(X_b \ge L-i+1\)
- 取 \(j = \max(0, \min(m+1, L-i+1))\)
- \(t(a, i, 1) \to (b, j, 0)\)
- 如果 \(X_b \lt i\),则 \(X_a \ge L-i+1\)
- 取 \(j = \max(0, \min(m+1, L-i+1))\)
- \(t(b, i, 1) \to (a, j, 0)\)
对于 \(X_a + X_b \le R\):
对于 \(0 \le i \le m+1\),
- 如果 \(X_a \ge i\),则 \(X_b \lt R-i+1\)
- 取 \(j = \max(0, \min(m+1, R-i+1))\)
- \(t(a, i, 0) \to t(b, j, 1)\)
- 如果 \(X_b \ge i\),则 \(X_a \lt R-i+1\)
- 取 \(j = \max(0, \min(m+1, R-i+1))\)
- \(t(b, i, 0) \to t(a, j, 1)\)
另外就是拓扑排序,
因为 tarjan 的时候是 \(t(i,j,0)\) 比 \(t(i,j,1)\) 先 tarjan,所以:
- 如果 \(t(i,j,0)\) 可达 \(t(i,j,1)\),则 \(t(i,j,1)\) 所属的连通块编号小于 \((t(i,j,0))\);
- 如果不可达,则 \(t(i,j,0)\) 所属的连通块编号小于 \((t(i,j,1))\)
也就是说,从小到大枚举 \(j\),找到最大的那个满足 \(X_i \ge j\) 的 \(j\):
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m+1; j++) {
if (bl[ t[i][j][0] ] == bl[ t[i][j][1] ]) {
puts("-1");
return 0;
}
if (bl[ t[i][j][0] ] < bl[ t[i][j][1] ])
ans[i] = j;
}
}
或者,从大到小枚举 \(j\),找到最小的那个满足 \(X_i \lt j\) 的 \(j-1\):
for (int i = 1; i <= n; i++) {
for (int j = m+1; j >= 0; j--) {
if (bl[ t[i][j][0] ] == bl[ t[i][j][1] ]) {
puts("-1");
return 0;
}
if (bl[ t[i][j][0] ] > bl[ t[i][j][1] ])
ans[i] = j-1;
}
}
(在完整代码的第 \(73 \sim 82\) 行)
这里,bl[x]
表示 \(x\) 所在连通块的编号。
示例程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2.2e4, maxm = maxn * 102 * 2;
int n, m, q, t[maxn][102][2], idx, ans[maxm];
vector<int> g[maxm];
int dfn[maxm], low[maxm], ts, bl[maxm], scc;
bool ins[maxm];
stack<int> stk;
void init() {
for (int i = 1; i <= n; i++)
for (int j = 0; j <= m+1; j++)
for (int k = 0; k < 2; k++)
t[i][j][k] = ++idx;
}
void add(int u, int v) {
g[u].push_back(v);
}
void tarjan(int u) {
dfn[u] = low[u] = ++ts;
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 (dfn[u] == low[u]) {
scc++;
int v;
do {
v = stk.top();
stk.pop();
ins[v] = false;
bl[v] = scc;
} while (u != v);
}
}
int main() {
scanf("%d%d%d", &n, &m, &q);
init();
for (int i = 1; i <= n; i++) {
add(t[i][0][1], t[i][0][0]);
add(t[i][m+1][0], t[i][m+1][1]);
for (int j = 1; j <= m+1; j++) {
add(t[i][j][0], t[i][j-1][0]);
add(t[i][j-1][1], t[i][j][1]);
}
}
while (q--) {
int a, b, L, R;
scanf("%d%d%d%d", &a, &b, &L, &R);
for (int i = 0; i <= m+1; i++) {
int j = max(0, min(m+1, L-i+1));
add(t[a][i][1], t[b][j][0]);
add(t[b][i][1], t[a][j][0]);
j = max(0, min(m+1, R-i+1));
add(t[a][i][0], t[b][j][1]);
add(t[b][i][0], t[a][j][1]);
}
}
for (int i = 1; i <= idx; i++)
if (!dfn[i])
tarjan(i);
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m+1; j++) {
if (bl[ t[i][j][0] ] == bl[ t[i][j][1] ]) {
puts("-1");
return 0;
}
if (bl[ t[i][j][0] ] < bl[ t[i][j][1] ])
ans[i] = j;
}
}
for (int i = 1; i <= n; i++)
printf("%d ", ans[i]);
return 0;
}