Розбір
Problem Author: | Illia Shevchenko |
Prepared by: | Maksym Shvedchenko |
Editorial by: | Maksym Shvedchenko |
To determine the degree of a vertex among the vertices , we can use a query. By leveraging the permutation and its reverse, we can compute the degree of each vertex.
Next, we iteratively identify a leaf, find its neighbor, and remove it from consideration. For any given leaf, based on multiple random permutations, we know that its neighbor must either be immediately to its left or right in at least one of these permutations. By intersecting these candidate sets across all permutations, we can uniquely identify the true neighbor.
If we use (2 queries was used to identify the degree) random permutations, the probability of incorrectly identifying a non-neighbor as the true neighbor decreases exponentially, specifically to . Thus, with , the probability of error becomes negligible.
#include <bits/stdc++.h> #define all(a) a.begin(),a.end() #define len(a) (int)(a.size()) #define mp make_pair #define pb push_back #define fir first #define sec second #define fi first #define se second using namespace std; typedef pair<int, int> pii; typedef long long ll; typedef long double ld; template<typename T> bool umin(T &a, T b) { if (b < a) { a = b; return true; } return false; } template<typename T> bool umax(T &a, T b) { if (a < b) { a = b; return true; } return false; } #ifdef KIVI #define DEBUG for (bool _FLAG = true; _FLAG; _FLAG = false) #define LOG(...) print(#__VA_ARGS__" ::", __VA_ARGS__) << endl template <class ...Ts> auto &print(Ts ...ts) { return ((cerr << ts << " "), ...); } #else #define DEBUG while (false) #define LOG(...) #endif const int max_n = 2011, inf = 1000111222; int n; inline vector <int> ask (vector <int> &p) { cout << "?"; for (int i : p) { cout << ' ' << i + 1; } cout << endl; vector <int> res(n); for (int i = 0; i < n; i++) { cin >> res[i]; } return res; } mt19937 rng(228); template<typename T = int> inline T randll(T l = INT_MIN, T r = INT_MAX) { return uniform_int_distribution<T>(l, r)(rng); } inline ld randld(ld l = INT_MIN, ld r = INT_MAX) { return uniform_real_distribution<ld>(l, r)(rng); } vector <int> edge[max_n]; int main() { // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); ios_base::sync_with_stdio(0); cin.tie(0); cin >> n; vector <int> p(n); iota(all(p), 0); auto res1 = ask(p); reverse(all(p)); auto res2 = ask(p); vector <int> deg(n); for (int i = 0; i < n; i++) { deg[i] += 1 - res1[i]; if (i) { deg[i] += res1[i - 1]; } } for (int i = 0; i < n; i++) { deg[i] += 1 - res2[n - i - 1]; if (i + 1 < n) { deg[i] += res2[n - (i + 1) - 1]; } } vector <vector <int> > query, gg; for (int i = 0; i < 48; i++) { shuffle(all(p), rng); gg.pb(p); query.pb(ask(p)); } queue <int> q; for (int i = 0; i < n; i++) { if (deg[i] == 1) { q.push(i); } } vector <pii> ans; vector <int> who(n), was(n); int timer = 0; for (int it = 0; it + 1 < n; it++) { int v = q.front(); q.pop(); for (int i = 0; i < n; i++) { who[i] = i != v; } int y = 0; for (auto &x : query) { ++timer; for (int i = 0; i < n; i++) { if (v == gg[y][i]) { int val = 1 - x[i]; if (i) { val += x[i - 1]; } for (int to : edge[v]) { if (was[to] == timer) { --val; } } if (val == 0) { for (int j = 0; j < i; j++) { who[gg[y][j]] = 0; } } else { assert(val == 1); for (int j = i + 1; j < n; j++) { who[gg[y][j]] = 0; } } break; } was[gg[y][i]] = timer; } ++y; } int pos = -1; for (int i = 0; i < n; i++) { if (who[i]) { assert(pos == -1); pos = i; } } ans.pb({pos, v}); edge[pos].pb(v); edge[v].pb(pos); --deg[pos]; if (deg[pos] == 1) { q.push(pos); } } cout << "!\n"; for (auto &i : ans) { cout << i.first + 1 << ' ' << i.second + 1 << '\n'; } }