2026天梯赛L3-2花砖物语题解
本题的质量相比起其它题简直是断崖式提升,赛后补知识点补得我头疼
赛后才知道是SUA出的题...那没事了
悲报:本人赛时想到了一个能过16分的神秘贪心解法,但是因为忘记初始化,找了半个小时都找不到bug,痛失16分
引入
讲解本题前,需要引入关于二分图的几个知识点
-
二分图的最大匹配
定义:在一个二分图 \(G=(U \cup V, E)\) 中,匹配(Matching)是指一组没有公共端点的边的集合。最大匹配就是包含边数最多的匹配
-
点覆盖
定义:点覆盖(Vertex Cover)是指一个顶点子集 C,使得图中每条边至少有一个端点属于 C。最小点覆盖就是顶点数最少的点覆盖。
通俗理解:选择最少的顶点来"覆盖"所有边(每条边至少有一个端点被选中)。
-
König 定理
二分图的最大匹配=最小点覆盖数
-
二分图的最大独立集
定义:最大独立集问题是指,在一张无向图中选择最多的顶点,满足两两之间互不相邻.
性质:最大独立集是最小点覆盖的补集,即:最大独立集+最小点覆盖 = n
了解更多讲解与证明请查阅OIWiki
思路
从工厂拿取的操作能且只能进行n次,所以我们需要最小化从桌面拿取的次数
最终答案为 n+桌面拿取次数
在桌面上,所有相同种类的砖可以看作是同一个点,因为一次操作作用于所有同一种类上
-
建图
将同一种类看作是一个点,在同一工厂的两个种类建一条边
容易看出,建出的图正好是一个二分图
工厂操作最优是拿取所有同一种类的转,即删去一个顶点与其连接的边,没有边的时候,工厂操作停止
而我们的目的是,最小化工厂操作后剩余的点数,也就是最大化工厂操作删去的点数
于是,工厂操作的目标是选取最多的顶点,且满足顶点之间两两不相连。即:选取最大独立子集
最后,留在桌上的点即为最小点覆盖
所以,答案为 n+最小点覆盖 ,也就是 n+最大匹配,匈牙利算法即可求解最大匹配
于是题目到此为止,恭喜恭喜!
但是!!!
你愉快地套上匈牙利算法的板子,你就会愉快的发现,被卡了!
因为匈牙利算法的时间复杂度是 \(O(VE)\) ,在本题的数据量下,是过不去的。
于是,请使用基于最大流的Hopcroft–Karp 算法,可以将复杂度降低到 \(O(V^{\frac{1}{2}}E)\)
但是当你补习完最大流,并且理解了Hopcroft–Karp 算法 是如何基于网络流寻找最大匹配,于是愉快地套上模板交题,你就会发现
卡常了!!!
这里我推荐你使用这个引入随机化和神秘常数优化的板子,就能愉快地通过这道题了,恭喜恭喜(瘫)
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int LL
struct HopcroftKarp
{
int n, m;
vector<array<int, 2>> ver;
vector<int> l, r;
HopcroftKarp(int n, int m) : n(n), m(m)
{
l.assign(n, -1);
r.assign(m, -1);
}
void add(int x, int y)
{
x--, y--;
ver.push_back({x, y});
}
int work()
{
vector<int> adj(ver.size());
mt19937 rgen(chrono::steady_clock::now().time_since_epoch().count());
shuffle(ver.begin(), ver.end(), rgen);
vector<int> deg(n + 1);
for (auto &[u, v] : ver)
deg[u]++;
for (int i = 1; i <= n; i++)
deg[i] += deg[i - 1];
for (auto &[u, v] : ver)
adj[--deg[u]] = v;
int ans = 0;
vector<int> a, p, q(n);
while (true)
{
a.assign(n, -1), p.assign(n, -1);
int t = 0;
for (int i = 0; i < n; i++)
{
if (l[i] == -1)
{
q[t++] = a[i] = p[i] = i;
}
}
bool match = false;
for (int i = 0; i < t; i++)
{
int x = q[i];
if (~l[a[x]])
continue;
for (int j = deg[x]; j < deg[x + 1]; j++)
{
int y = adj[j];
if (r[y] == -1)
{
while (~y)
{
r[y] = x;
swap(l[x], y);
x = p[x];
}
match = true;
ans++;
break;
}
if (p[r[y]] == -1)
{
q[t++] = y = r[y];
p[y] = x;
a[y] = a[x];
}
}
}
if (!match)
break;
}
return ans;
}
vector<array<int, 2>> answer()
{
vector<array<int, 2>> ans;
for (int i = 0; i < n; i++)
{
if (~l[i])
{
ans.push_back({i + 1, l[i] + 1}); // 转回 1-index 输出
}
}
return ans;
}
};
void solve()
{
int n;
cin >> n;
HopcroftKarp flow(n, n);
for (int i = 1; i <= n; i++)
{
int u, v;
cin >> u >> v;
flow.add(u, v);
}
cout << n + flow.work() << endl;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
}

浙公网安备 33010602011771号