[QOJ4218] Hidden Graph 题解
观察题目约束:每个导出子图存在一个度数 \(\le k\) 的点。
这个约束可以转化为:图可以被 \(k + 1\) 染色,因为每次选择度数最小的点,先对除了它之外的点进行染色,最后给它染色,这个构造还给出了这张图边数上界是 \(nk\)。
考虑增量构造,每次加入一个点 \(i\),处理出 \(1\sim i - 1\) 构成的颜色集合,每个集合内部没有边,所以对每个集合找到 \(i\) 和其中点的所有连边,即:
- 询问这个集合 \(s\) 和 \(i\),如果找不到边就跳出
- 记录这条边 \(u-i\), 在 \(s\) 中删掉 \(u\),回到步骤 1
这样我们可以在 \(O(n ^ 2k\log n)\) 的时间内完成询问。
因为这张图最多有 \(nk\) 条边,每条边被询问了一次,对于每个 \(i\),他需要询问 \(k + 1\) 个颜色集合,操作次数刚好是 \(nk + n(k + 1)\)。
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <numeric>
#define x first
#define y second
#define int long long
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 2000 + 10;
bool Mst;
int n, d[N], c[N], k, mn;
vector<int> idp[N], g[N];
bool st[N];
priority_queue<PII, vector<PII>, greater<PII> > heap;
void dfs(int n) {
if(!n) return ;
while(heap.size() && st[heap.top().y]) heap.pop();
auto u = heap.top().y; heap.pop();
for(auto x : g[u])
d[x] --, heap.push({d[x], x});
st[u] = 1;
dfs(n - 1);
set<int> s;
for(auto x : g[u]) s.insert(c[x]);
for(int i = 1; ; i ++)
if(s.find(i) == s.end()) {
c[u] = i;
break;
}
}
void recolor(int n) {
while(heap.size()) heap.pop();
for(int i = 1; i <= n; i ++) d[i] = 0, c[i] = 0, st[i] = 0, idp[i].clear();
mn = 1;
for(int i = 1; i <= n; i ++) for(auto j : g[i]) d[j] ++;
for(int i = 1; i <= n; i ++) heap.push({d[i], i});
dfs(n);
for(int i = 1; i <= n; i ++) k = max(k, c[i]), idp[c[i]].push_back(i);
}
PII ask(vector<int> v, int p) {
cout << "? "; cout << v.size() + 1 << ' ';
for(auto x : v) cout << x << ' '; cout << p << ' ';
cout << endl;
int x, y;
cin >> x >> y;
return {x, y};
}
bool Med;
signed main() {
ios::sync_with_stdio(0), cin.tie(0);
cerr << abs(&Mst - &Med) / 1048576. << "Mb\n";
cin >> n;
int m = 0;
for(int i = 2; i <= n; i ++) {
recolor(i - 1);
for(int j = 1; j <= k; j ++) {
vector<int> v = idp[j];
while(v.size()) {
auto t = ask(v, i);
if(t.x == -1) break;
if(t.x == i) swap(t.x, t.y);
m ++, g[t.x].push_back(i), g[i].push_back(t.x);
v.erase(lower_bound(v.begin(), v.end(), t.x));
}
}
}
cout << "! " << m << '\n';
for(int i = 1; i <= n; i ++)
for(auto j : g[i]) if(i < j) cout << i << ' ' << j << '\n';
cout << endl;
return 0;
}

QwQ
浙公网安备 33010602011771号