根据 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;
}
标签:int,题解,back,else,ask,CF1977E,push,stk
From: https://www.cnblogs.com/hcywoi/p/18423061