根据 Dilworth 定理,该图能被两条互不相交的链覆盖。
从小到大加点。我们现在需要维护两个栈,每个栈维护每条链的点。
若两个栈头没有连边,那么对于新加入的点,一定可以放到其中一个栈。
现在唯一的问题是,新加入的点可能可以放入两个栈。
我们可以再开一个栈三,用来维护以上述点为头的链。
对于一个新加入的点,分以下情况:
-
若栈三为空。分别与前两个栈的栈头询问。分情况加入三个栈即可。
-
若栈三不为空。先询问是否与栈三的栈头相连。若相连,则加入栈三。否则,加入前两个栈中栈头与其相连的栈中(若有两个,任选一个即可)。然后将栈三的所有元素加入到与其相反的栈中。
容易发现,这样构造一定能使得前两个栈的栈头不相连。对于每个点,最多询问 \(2\) 次。所以总的询问次数不多于 \(2n\)。
#include <bits/stdc++.h>using i64 = long long;int ask(int a, int b) {std::cout << "? " << a + 1 << " " << b + 1 << std::endl;std::string s;std::cin >> s;return s == "YES";
}void solve() {int n;std::cin >> n;std::vector<int> c(n);std::vector<int> stk[3];stk[0].push_back(0);for (int i = 1; i < n; i ++ ) {if (stk[1].empty()) {if (ask(stk[0].back(), i)) {stk[0].push_back(i);} else {stk[1].push_back(i);}} else {if (stk[2].empty()) {int x = ask(stk[0].back(), i);int y = ask(stk[1].back(), i);if (!x) {stk[1].push_back(i);} else if (!y) {stk[0].push_back(i);} else {stk[2].push_back(i);}} else {if (ask(stk[2].back(), i)) {stk[2].push_back(i);} else if (ask(stk[0].back(), i)) {stk[0].push_back(i);for (auto j : stk[2]) {stk[1].push_back(j);}stk[2].clear();} else {stk[1].push_back(i);for (auto j : stk[2]) {stk[0].push_back(j);}stk[2].clear();}}}}for (auto i : stk[1]) {c[i] = 1;}std::cout << "!";for (int i = 0; i < n; i ++ ) {std::cout << " " << c[i];}std::cout << std::endl;
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int t;std::cin >> t;while (t -- ) {solve();}return 0;
}