CF2061G Kevin and Teams 题解

CF2061G Kevin and Teams 题解

题目:click here

首先第一步考虑这道题如何计算 \(k\) ,另朋友关系为红边,非朋友关系为蓝边,则我们考虑为了方便地构造,可以设 \(x\) 个点组成一个红边完全图, \(n-x\) 个点组成蓝边完全图,两个之间用蓝边连接,于是对于红边,\(\left\lfloor \frac{x}{2} \right\rfloor \le k\) 我们可以得到 \(x \le 2k+1\) 而对于蓝边,我们首先用红连通块的点连接蓝色的连通块,然后再内部两两连接,于是:

\[\min\left\{x,n-x\right\}+\left\lfloor \frac{\max\left\{(n-x)-x, 0\right\}}{2} \right\rfloor \le k \]

我们分类讨论,如果 \(n-x < x\) 那么我们可以得到

\[n-x \le k \]

带入第一个式子得到

\[k \ge n-x \ge n - 2k - 1 \implies n \le 3k + 1 \implies k \ge \left\lceil \frac{n - 1}{3} \right\rceil = \left\lfloor \frac{n + 1}{3} \right\rfloor \]

对于 \(n-x > x\) 我们得到

\[x+ \left\lfloor \frac{n}{2} - x\right\rfloor=\left\lfloor \frac{n}{2}\right\rfloor \le k \]

显然是不可能存在的,于是我们可以取 \(k=\left\lfloor \frac{n+1}{3} \right\rfloor\)

接下来考虑如何构造答案,我们得到这个 \(k\) 首先可以想到是每三个节点出一个配对,考虑对于三个结点 \(a,b,c\) 如果 \((a,b) \ne (b,c)\) 那么就可以丢掉这三个点,并贡献1的答案,剩余的跑出来的答案恰好是 \(k-1\) 但如果相等,我们可以先按下不表,用同色链存下来,直到遇到下一个不同色的边,最后同色链剩余的两两配对,得到的答案一定足够,因为 \(\left\lfloor \frac{n}{2} \right\rfloor \ge \left\lfloor \frac{n+1}{3} \right\rfloor\)

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int query(int u, int v) {
    cout << '?' << ' ' << u << ' ' << v << '\n';
    cout.flush();
    int x; cin >> x;
    return x;
}
void solve() {
    int n;
    cin >> n;
    cout << (n + 1) / 3 << '\n';
    cout.flush();
    stack<pair<int, int>> stk;
    vector<pair<int, int>> ans0, ans1;

    for (int i = 1; i <= n; i++) {
        if (stk.size() == 0) {
            stk.push({i, 0});
        }
        else if (stk.size() == 1) {
            int x = query(stk.top().first, i);
            stk.push({i, x});
        }
        else {
            int x = query(stk.top().first, i);
            if (stk.top().second == x) {
                stk.push({i, x});
            }
            else {
                int ed = stk.top().first; int w = stk.top().second; stk.pop();
                int u = stk.top().first; stk.pop();
                if (w == 0) ans0.push_back({u, ed}), ans1.push_back({ed, i});
                else ans1.push_back({u, ed}), ans0.push_back({ed, i});
            }
        }
    }
    while (stk.size() >= 2) {
        int ed = stk.top().first, w = stk.top().second; stk.pop();
        int u = stk.top().first; stk.pop();
        if (w == 0) ans0.push_back({u, ed});
        else ans1.push_back({u, ed});
    }
    cout << "! ";
    if (ans0.size() > ans1.size()) {
        for (int i = 0; i < (n + 1) / 3; i++) {
            cout << ans0[i].first << ' ' << ans0[i].second << ' ';
        }
    }
    else {
        for (int i = 0; i < (n + 1) / 3; i++) {
            cout << ans1[i].first << ' ' << ans1[i].second << ' ';
        }
    }
    cout << '\n';
    cout.flush();
}
int main() { 
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}
posted @ 2026-09-07 16:58  MikotoHohenz  阅读(14)  评论(0)    收藏  举报