#include using namespace std; using ll=long long; const ll ILL=2167167167167167167; const int INF=2100000000; #define rep(i,a,b) for (int i=(int)(a);i<(int)(b);i++) #define all(p) p.begin(),p.end() template using _pq = priority_queue, greater>; template int LB(vector &v,T a){return lower_bound(v.begin(),v.end(),a)-v.begin();} template int UB(vector &v,T a){return upper_bound(v.begin(),v.end(),a)-v.begin();} template bool chmin(T &a,T b){if(b bool chmax(T &a,T b){if(a void So(vector &v) {sort(v.begin(),v.end());} template void Sore(vector &v) {sort(v.begin(),v.end(),[](T x,T y){return x>y;});} bool yneos(bool a,bool upp=false){if(a){cout<<(upp?"YES\n":"Yes\n");}else{cout<<(upp?"NO\n":"No\n");}return a;} template void vec_out(vector &p,int ty=0){ if(ty==2){cout<<'{';for(int i=0;i<(int)p.size();i++){if(i){cout<<",";}cout<<'"'< T vec_min(vector &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmin(ans,x);return ans;} template T vec_max(vector &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmax(ans,x);return ans;} template T vec_sum(vector &a){T ans=T(0);for(auto &x:a) ans+=x;return ans;} int pop_count(long long a){int res=0;while(a){res+=(a&1),a>>=1;}return res;} template T square(T a){return a * a;} //https://ei1333.github.io/library/other/xor-shift.cpp.html struct XorShift { private: constexpr static double R = 1.0 / 0xffffffff; uint64_t x; public: explicit XorShift(uint64_t seed = 88172645463325252ull) : x(seed) {} template< typename T = uint64_t > inline T get() { // [0, 2^64) x ^= x << 7ull; x ^= x >> 9ull; return x; } inline uint32_t get(uint32_t r) { // [0, r) return ((uint64_t) get< uint32_t >() * r) >> 32ull; } inline uint32_t get(uint32_t l, uint32_t r) { // [l, r) return l + get(r - l); } inline double probability() { // [0.0, 1.0] return get< uint32_t >() * R; } }; XorShift rnd; namespace po167{ struct UF { using _F = int; int _n; std::vector<_F> wei; std::vector q; int component; UF(int n):_n(n), wei(n), component(n), par(n){ for (int i = 0; i < n; i++){ wei[i] =1, par[i] = i; } } void intialize(){ for (auto x : q){ wei[root(x)] = 1; par[x] = x; wei[x] = 1; } component = (int)par.size(); q = {}; } //根っこを返す int root(int a){ assert(0 <= a && a < _n); if (a == par[a]) return a; return par[a] = root(par[a]); } //trueなら1,falseなら0 int same(int a, int b){ assert(0 <= a && a < _n); assert(0 <= b && b < _n); if(root(a) == root(b)) return 1; else return 0; } _F size(int a){ return wei[root(a)]; } //a,bが違う根っこの元なら結合する,結合したらtrueを返す bool unite(int a,int b){ a = root(a), b = root(b); if (a == b) return false; // if (wei[a] < wei[b]) std::swap(a, b); par[b] = a; q.push_back(b); wei[a] += wei[b]; component--; return true; } private: std::vector par; }; } using po167::UF; template void shuffle_vector(vector &p,XorShift &table){ int L=p.size(); for(int i=0;i> t; rep(i, 0, t) solve(); } void solve(){ int N; cin >> N; vector order(N); rep(i, 0, N) order[i] = i; bool local = false; vector ans(N), inv(N); if (local){ rep(i, 0, N) ans[i] = i; shuffle_vector(ans, rnd); rep(i, 0, N) inv[ans[i]] = i; } int C = 0; map, int> m; auto ask = [&](int a, int b) -> int { if (a > b) swap(a, b); if (m.count({a, b})) return m[{a, b}]; C++; cout << "1 " << a + 1 << " " << b + 1 << endl; if (local){ int s = ans[a] + ans[b] + 2; if (s > N){ m[{a, b}] = -1; return -1; } m[{a, b}] = inv[s - 1]; return inv[s - 1]; } int res; cin >> res; m[{a, b}] = res - 1; return res - 1; }; while ((int)order.size() > 2){ if ((int)order.size() == 3){ while (ask(order[0], order[1]) != order[2]){ swap(order[0], order[1]); swap(order[1], order[2]); } order.pop_back(); break; } vector safe(N); for (auto x : order) safe[x] = 1; while (ask(order[0], order[1]) < 0){ shuffle_vector(order, rnd); } UF T(N); int Z = 0; rep(j, 1, order.size()){ int tmp = ask(order[0], order[j]); if (tmp < 0 || safe[tmp] == 0){ Z++; } else{ T.unite(order[j], tmp); } } if (Z == 1){ order = {order[0]}; break; } vector n_order; int val = 0; rep(j, 1, order.size()){ if (T.root(order[j]) != order[j]) continue; if (chmax(val, T.size(order[j]))) n_order.clear(); if (val == T.size(order[j])) n_order.push_back(order[j]); } swap(n_order, order); }vector roots; vector> G(N); rep(i, 0, N){ if (i == order[0]) continue; auto tmp = ask(i, order[0]); if (tmp < 0) roots.push_back(i); else G[tmp].push_back(i); } vector p(N); // vec_out(ans); // cout << roots.size() << " " << order.size() << endl; if (roots.size() == 2){ p[order[0]] = 2; if (ask(roots[0], order[1]) < 0){ swap(roots[0], roots[1]); } p[roots[0]] = N - 1; p[roots[1]] = N; } else{ p[order[0]] = 1; p[roots[0]] = N; } for (auto x : roots){ while (!G[x].empty()){ p[G[x][0]] = p[x] - (int)roots.size(); x = G[x][0]; } } cout << "2"; for (auto x : p) cout << " " << x; cout << endl; if (local){ rep(i, 0, N) ans[i]++; yneos(ans == p); cout << C << " / " << 3 * N << endl; } } /* * * N = 5 * 2 -> 3 -> 4 -> 5 * * 1 -> 3 -> 5 * 4 * * 1 -> 4 * 2 -> 5 * * 1 -> 5 * 2 * 3 * * 1 * 2 * 3 * 4 * * N = 6 * 1 -> 3 -> 5 * 4 -> 6 * * 1 -> 4 * 2 -> 5 * 6 * * 1 -> 5 * 2 -> 6 * 3 * * */