QOJ2605 题解
题意:给定 \(n,m,k\),给出一个 \(n\) 个点 \(m\) 条边的无向图,要求给每个点染成红、蓝、灰三种颜色之一,使得:
- 和每个红点相连的蓝点个数不少于 \(k+1\);
- 和每个蓝点相连的红点个数不少于 \(k+1\);
保证 \(m\ge 2nk\)。
对于一个无向图 \(G\),我们总可以把点集划分成两部分 \(S,T\),使得两端不在同一个集合内的边的个数至少是 \(\frac{m}{2}\)。这个增量构造即可。
对应到本题中,也就是我们可以给每个点按照上述方式染成蓝色/红色,使得蓝红两个集合之间的边数至少为 \(nk\)。
考虑先这样染色后再调整。如果当前仍然不合法,那么必定有一个点 \(x\) 满足 \(x\) 的度数(这里只考虑连向另一个集合的边)小于 \(k+1\)。直接删去这个点以及和他相连的边。
因为每次删去的边数不超过 \(k\),所以“边数不小于点数的 \(k\) 倍”这一条件总是成立。于是可以推出 \(kn\le m\le \frac{n^2}{4}\),这意味着剩下的点数 \(n\) 不会小于 \(4k\),因此调整一定会结束,这样我们就得到了一个合法的染色方案。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10;
int n, m, k, col[N], d[N];
vector<int> g[N];
void solve()
{
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) g[i].clear(), col[i] = 0;
for (int i = 1, u, v; i <= m; i++) cin >> u >> v, g[u].push_back(v), g[v].push_back(u);
for (int i = 1; i <= n; i++)
{
int c[2]{};
for (int x : g[i])
if (x < i) c[col[x]]++;
if (c[0] > c[1]) col[i] = 1;
else col[i] = 0;
}
struct S
{
int d, i;
bool operator< (const S& s2) const
{
return d == s2.d ? i < s2.i : d < s2.d;
}
};
set<S> s;
memset(d, 0, sizeof(int) * (n + 1));
for (int i = 1; i <= n; i++)
{
for (int x : g[i])
if (col[x] != col[i]) d[i]++;
s.insert({d[i], i});
}
while (s.begin()->d <= k)
{
auto it = s.begin();
int i = it->i;
for (int x : g[i])
if (col[x] == (col[i] ^ 1)) s.erase({d[x], x}), d[x]--, s.insert({d[x], x});
s.erase(it), col[i] = -1;
}
vector<int> r, b;
for (int i = 1; i <= n; i++)
{
if (col[i] == 0) b.push_back(i);
if (col[i] == 1) r.push_back(i);
}
cout << r.size() << '\n';
for (int x : r) cout << x << ' '; cout << '\n';
cout << b.size() << '\n';
for (int x : b) cout << x << ' '; cout << '\n';
}
signed main()
{
int t; cin >> t;
while (t--) solve();
}
浙公网安备 33010602011771号