xy0v0's Blog

Back

题目

https://qoj.ac/contest/1849/problem/9783/statement/zh_cn

思路

记 ​d(u,v) 代表 ​u,v 节点之间的距离,不难注意到: ​query(\mathbf A) + query (\mathbf B) \ne query (\mathbf A \cup \mathbf B) 等价于 ​\exist a \in \mathbf A,b \in \mathbf B: d(a,b) \le 2 ,此处略去证明。

如此我们可最多通过三次查询确定两个点集之间的连通情况。

定义 ​\mathbf S := \{u | \forall u,v \in \mathbf S: u与v连通\} ,且初始不妨设 ​\mathbf S = \{1\} .

当 ​|S| \ne |V| 时,考虑每次加入一个点,首先若 ​query(\mathbf S)=0 知不连通,直接结束。若 ​query(\mathbf S) \ne 0 ,那么令 ​\mathbf S' := \mathbf V \backslash \mathbf S ,将 ​\mathbf S' 均分为两个集合 ​\mathbf {L,R} ,不难证明 ​\mathbf S 与两集合中至少一个连通,由上述方法判断与 ​\mathbf L 是否连通 ,否则,必定与 ​\mathbf R 连通,以此类推,必定能找到一个连通的点加入。

分析总操作次数,最坏情况为 ​(n-1)(1+2 [\log_2(n-1)]) < 3500 ,可通过。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool vst[209];
int v;
int cnt;
int ask(string &s) {
    cout << "? " << s << endl;
    int res;
    cin >> res;
    return res;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> v;
    string s(v, '0');
    s[0] = '1';
    cnt = 1;
    while (cnt < v) {
        int q = ask(s);
        if (q == 0) {
            cout << "! 0" << endl;
            return 0;
        }
        vector<int> c;
        for (int i = 0; i < v; i++) {
            if (s[i] == '0')
                c.push_back(i);
        }
        int l = 0, r = c.size();
        while (l < r - 1) {
            int mid = (l + r) >> 1;
            string s1(v, '0'), s2 = s;
            for (int i = l; i < mid; i++) {
                s1[c[i]] = '1';
                s2[c[i]] = '1';
            }
            int q1 = ask(s1), q2 = ask(s2);
            if (q + q1 != q2)
                r = mid;
            else
                l = mid;
        }
        s[c[l]] = '1';
        cnt++;
    }
    cout << "! 1" << endl;
    return 0;
}

补题 | Ucup 3 St.18 C
https://www.xy0v0.top/archives/ucup3st18c
Author xy0v0
Published at 十月 7, 2026