[QOJ4218] Hidden Graph 题解

观察题目约束:每个导出子图存在一个度数 \(\le k\) 的点。

这个约束可以转化为:图可以被 \(k + 1\) 染色,因为每次选择度数最小的点,先对除了它之外的点进行染色,最后给它染色,这个构造还给出了这张图边数上界是 \(nk\)

考虑增量构造,每次加入一个点 \(i\),处理出 \(1\sim i - 1\) 构成的颜色集合,每个集合内部没有边,所以对每个集合找到 \(i\) 和其中点的所有连边,即:

  1. 询问这个集合 \(s\)\(i\),如果找不到边就跳出
  2. 记录这条边 \(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;
}

posted @ 2025-06-20 10:07  MoyouSayuki  阅读(15)  评论(2)    收藏  举报
:name :name