题解:CF1977E Tensor
Priestess_SLG · · 题解
首先有一个经典结论:连通性具有传递性。具体来说,图上若
考虑维护两个栈
- 若
S_1 栈为空,那么将i 放入S_1 栈的栈顶。 - 若
S_2 栈为空,那么将i 放入S_2 栈的栈顶。 - 若
S_1 栈栈顶元素和i 连通,那么将i 放入S_1 栈的栈顶。 - 若
S_2 栈栈顶元素和i 连通,那么将i 放入S_2 栈的栈顶。 - 若上述四个简单的条件均不满足,则考虑一直暴力的将
S_1 栈栈顶的元素丢入栈S_2 的栈顶,直到栈S_1 为空,或S_1 栈顶元素和i 连通为止。此时将i 放入S_1 栈的栈顶。
下面来证明这个看似胡编乱造的策略的正确性:
一、交互次数
首先可以简单发现前两种操作不会耗费任何询问次数,三、四种操作只会耗费一次询问次数。问题在于五操作。若对每一次询问的答案进行记忆化处理,则可以发现,此时
二、时间复杂度
交互次数是对的,那么因为时间复杂度完全等同于交互次数,所以为
三、正确性分析
若不存在最后一种操作,则因为
问题在于最后一个操作。考虑到该图的特殊性,任意三个点
但是此时无法保证 set 来提到栈维护
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 110;
int res[N], n, vis[N][N];
int ok(int i, int j) {
if (vis[i][j] == 1) return 1;
if (vis[i][j] == 0) return 0;
cout << "? " << i << ' ' << j << endl;
string o; cin >> o;
return vis[i][j] = vis[j][i] = (o == "YES");
}
signed main() {
// cin.tie(0)->sync_with_stdio(false);
cout << fixed << setprecision(15);
cerr << fixed << setprecision(5);
int T;
cin >> T;
while (T--) {
memset(vis, -1, sizeof vis);
cin >> n;
stack<int> s1, s2;
for (int i = 1; i <= n; ++i) {
if (s1.empty()) s1.emplace(i);
else if (s2.empty()) s2.emplace(i);
else if (ok(s1.top(), i)) s1.emplace(i);
else if (ok(s2.top(), i)) s2.emplace(i);
else {
while (s1.size() && !ok(s1.top(), i)) s2.emplace(s1.top()), s1.pop();
s1.emplace(i);
}
}
while (s1.size()) res[s1.top()] = 0, s1.pop();
while (s2.size()) res[s2.top()] = 1, s2.pop();
cout << "! ";
for (int i = 1; i <= n; ++i) cout << res[i] << ' ';
cout << endl;
}
return 0;
}