题解:AT_abc461_g [ABC461G] Graph Problem 2026
Limit_Yank_Peak · · 题解
[ABC461G] Graph Problem 2026 Solution
Part 0th. 简化题意
- 有
N 个顶点,M 条边的简单无向图 - 要给每个顶点
j 赋一个非负整数权值W_j - 约束:
W_j \le 2026 ,且每条边(u,v) 满足W_u + W_v \le 2026 - 目标:最大化
\sum_{j=1}^N W_j Part 1st. 初步分析
由于题目没有保证是一个连通图,所以我们先考虑连通图的情况。
瞪眼法可以发现无论什么情况下最劣情况的答案都是
观察样例,感觉最优的情况下
通过线性规划边界分析可以证明:存在一个最优解,每个点的权值只可能是
不知道什么是线性规划边界分析的点这里。 <- Click
:::info[Deepseek 的严谨证明]
记
1. 对偶问题与互补松弛
引入对偶变量:
-
\alpha_i \ge 0$ 对应约束 $W_i \ge 0 -
\beta_i \ge 0$ 对应约束 $-W_i \ge -C$,即 $W_i \le C -
\gamma_e \ge 0$ 对应约束 $W_u+W_v \le C
对偶规划为:
由线性规划的强对偶定理,存在最优原始解
-
W_i > 0 \;\Rightarrow\; \alpha_i = 0 -
W_i < C \;\Rightarrow\; \beta_i = 0 -
W_u+W_v < C \;\Rightarrow\; \gamma_{uv}=0
2. 分析内部点
取一个最优解
左边是非负项之和为
从而
3. 连通分量结构
考虑所有内部点(即
4. 最大化总和的调整
考虑
在保持其他分量不变的前提下,我们可以调整
因此,总存在一个最优解,使得每个连通分量要么取极值
5. 回到原题
由于
证毕。 :::
然后我们就可以把这一题压缩一下,变成给一个
Part 2nd. 问题转化
这不就是一个裸的二分图吗?
这种问题就可以直接用二分图解决了。
我们将
注意,二分图问题中你只需要从
当然,匈牙利 骗你的其实匈牙利跑的飞快的根本卡不掉所以要使用 Hopcroft-Karp 算法。
最后我们设最大匹配答案为
Part 3rd. 最终代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = (5e4 + 10) * 2;
const int INF = 1e9 + 91;
int n, m, pairU[MAXN], pairV[MAXN];
int dis[MAXN];
vector<int>G[MAXN];
bool bfs() {
queue<int>q;
for (int i = 0; i < n; i++) {
if (pairU[i] == -1) {
dis[i] = 0;
q.push(i);
} else {
dis[i] = INF;
}
}
bool flag = false;
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto v : G[u]) {
if (pairV[v] != -1 and dis[pairV[v]] == INF) {
dis[pairV[v]] = dis[u] + 1;
q.push(pairV[v]);
} else if (pairV[v] == -1) {
flag = true;
}
}
}
return flag;
}
bool dfs(int s) {
for (int v : G[s]) {
if (pairV[v] == -1 || (dis[pairV[v]] == dis[s] + 1 && dfs(pairV[v]))) {
pairU[s] = v;
pairV[v] = s;
return true;
}
}
dis[s] = INF;
return false;
}
int HK() {
fill(pairU, pairU + 2 * n, -1);
fill(pairV, pairV + 2 * n, -1);
int ans = 0;
while (bfs()) {
for (int u = 0; u < n; u++) {
if (pairU[u] == -1 && dfs(u)) {
ans++;
}
}
}
return ans;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
G[u - 1].emplace_back(v + n - 1);
G[v - 1].emplace_back(u + n - 1);
}
cout << (2 * n - HK()) * 1ll * 1013ll ;
return 0;
}