LOJ 3113 「SDOI2019」热闹的聚会与尴尬的聚会
首先这个 \(p, q, n\) 的限制看起来实在很奇怪。
所以说首先就要改一下限制的形式,那么就可以考虑把看起来比较难受的下取整拆掉:\(\lfloor\frac{n}{p + 1}\rfloor \le q \iff \frac{n}{p + 1} < q + 1\),同理另一个为 \(\frac{n}{q + 1} < p + 1\)。
于是只需要满足 \(n < (p + 1)(q + 1)\) 即可。
接下来能发现是可以用贪心直接求出来最优的 \(p\) 的:
具体来说,因为 \(p\) 是导出子图中的度数最小值,那么如果想要让 \(p\) 变大,那么度数最小值的点肯定都要被抛去。
于是贪心每次抛去度数最小值的点并且更新 \(p\) 一定是能得出 \(p\) 的最优值的。
那么接下来就是考虑求解 \(q\) 了。
虽然 \(q\) 看起来就不是很好求出最优值,但是因为题目只要求 \((p + 1)(q + 1) > n\),于是可以考虑套用 \(p\) 的信息来给 \(q\) 求出一个满足条件的值。
接下来,可以尝试手玩一些特殊情况,比如说我尝试的:
图由若干个团组成,那么此时 \(p\) 就为最大的团的大小 \(-1\)(最大度数),\(q\) 就为团的数量。
而此时的 \(p, q\) 一定满足条件,这是因为考虑到独立集每选入一个点就会删掉自己和邻点,那么相当于是删掉了 \(\operatorname{deg} + 1 = \operatorname{size}\) 个点,但此时 \(p = \operatorname{deg}_{\max}\),所以一定有 \(q \ge \frac{n}{p + 1}\)。
在分析了这个特殊情况后,就可以继续扩展至一般图了:
因为删掉一个点相当于是删掉了 \(\operatorname{deg} + 1\) 个点。
接下来套用 \(p\) 的信息:对于任意一个导出子图,\(\operatorname{deg}_{\min}\le p\)。
于是删掉度数最小值的点,至多删掉 \(p + 1\) 个点。
于是就有 \(q \ge \frac{n}{p + 1}\),于是就有 \((p + 1)(q + 1) > n\)。
用优先队列来实现删除度数最小值的点,时间复杂度 \(\mathcal{O}(T(n + m)\log (n + m))\)。
不要用 set 实现,会被卡常。
#include<bits/stdc++.h>
inline int read(int x = 0, int c = getchar_unlocked()) {
while (! isdigit(c)) c = getchar_unlocked();
while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar_unlocked();
return x;
}
constexpr int maxn = 1e4 + 10;
int n, m;
std::vector<int> G[maxn];
int p, q;
inline void solv1() {
static int deg[maxn], vis[maxn], ord[maxn];
memset(vis + 1, 0, sizeof(int) * n);
std::priority_queue<std::pair<int, int> > Q;
for (int i = 1; i <= n; i++) deg[i] = G[i].size(), Q.emplace(-deg[i], i);
int s = 0; p = 0;
for (int i = 1; i <= n; i++) {
while (vis[Q.top().second]++) Q.pop();
auto [_, u] = Q.top();
if (deg[u] > p) {p = deg[u], s = i;}
ord[i] = u;
for (int v : G[u]) {
deg[v]--;
Q.emplace(-deg[v], v);
}
}
printf("%d ", n - s + 1);
for (int i = s; i <= n; i++) printf("%d ", ord[i]);
puts("");
}
inline void solv2() {
static int deg[maxn], vis[maxn];
memset(vis + 1, 0, sizeof(int) * n);
std::priority_queue<std::pair<int, int> > Q;
for (int i = 1; i <= n; i++) deg[i] = G[i].size(), Q.emplace(-deg[i], i);
std::vector<int> V;
auto del =
[&](int u) {
if (vis[u]++) return ;
for (int v : G[u]) {
if (vis[v]) continue;
deg[v]--;
Q.emplace(-deg[v], v);
}
};
while (Q.size()) {
auto [_, u] = Q.top(); Q.pop();
if (vis[u]++) continue;
V.push_back(u);
del(u);
for (int v : G[u]) del(v);
}
q = V.size();
printf("%zu ", V.size());
for (int u : V) printf("%d ", u);
puts("");
}
inline void solve() {
n = read(), m = read();
for (int i = 1; i <= n; i++) std::vector<int>().swap(G[i]);
for (int i = 1, x, y; i <= m; i++) {
x = read(), y = read();
G[x].push_back(y), G[y].push_back(x);
}
solv1(), solv2();
fprintf(stderr, "p = %d, q = %d\n", p, q);
assert(n / (p + 1) <= q && n / (q + 1) <= p);
}
int main() {
int T; scanf("%d", &T);
while (T--) solve();
return 0;
}
浙公网安备 33010602011771号