CF772E Verifying Kingdom【互動,點分治】
阿新 • • 發佈:2021-06-26
給定正整數 \(n\),互動庫有 \(n\) 個葉子的"滿二叉樹"(所有點恰有 \(0/2\) 個兒子),也即 \(2n-1\) 個點。
每個葉子有 \(1-n\) 的編號,每次詢問 \(a_1,a_2,a_3\),互動器告訴你 \(\text{LCA}(a_1,a_2)\),\(\text{LCA}(a_2,a_3)\),\(\text{LCA}(a_3,a_1)\) 中哪個最淺。
求這棵樹,但不需求出編號(給出的樹與答案在有根意義下同構即可)。
\(n\le 10^3\)。
點分治?想不到。。。
按順序把葉子插入,維護虛樹。初始時 \(n+1\) 連向 \(1,2\)。
設當前要插葉子 \(i\)
時間複雜度 \(O(n^2)\),操作次數 \(n\lfloor\log_2n\rfloor\)。
#include<bits/stdc++.h> using namespace std; const int N = 2003; template<typename T> bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;} int n, S, Mn, rt, ct, cnt, siz[N], ch[N][2], fa[N], lef[N]; bool vis[N]; void link(int f, bool c, int x){ch[fa[x] = f][c] = x;} void fdrt(int x){ if(vis[x]){siz[x] = 0; return;} int ws; if(ch[x][0]){ int a = ch[x][0], b = ch[x][1]; fdrt(a); fdrt(b); siz[x] = siz[a]+siz[b]+1; ws = max(max(siz[a], siz[b]), S-siz[x]); } else { siz[x] = 1; ws = S-1; } if(chmin(Mn, ws)) ct = x; } void solve(int x, int f, bool c, int u){ vis[x] = true; int a = ch[x][0], b = ch[x][1], t; if(a){ printf("%d %d %d\n", lef[a], lef[b], u); fflush(stdout); do t = getchar(); while(t < 'X' || t > 'Z'); } if(!a || t == 'X'){ if(!fa[x] || vis[fa[x]]){ if(fa[x]) link(fa[x], c, ++cnt); else rt = ++cnt; link(cnt, 0, x); link(cnt, 1, u); lef[cnt] = lef[u] = u; } else { S -= siz[x]; Mn = N; fdrt(f ? ch[f][c] : rt); solve(ct, f, c, u); } } else { a = ch[x][t = t == 'Y']; if(vis[a]){ link(x, t, ++cnt); link(cnt, 0, a); link(cnt, 1, u); lef[cnt] = lef[u] = u; } else { S = siz[a]; Mn = N; fdrt(a); solve(ct, x, t, u); } } } int main(){ scanf("%d", &n); cnt = n; link(rt = ++cnt, 0, 1); link(rt, 1, 2); lef[1] = lef[rt] = 1; lef[2] = 2; for(int i = 3;i <= n;++ i){ memset(vis, 0, sizeof vis); S = i-2<<1|1; Mn = N; fdrt(rt); solve(ct, 0, 0, i); } puts("-1"); for(int i = 1;i <= cnt;++ i) printf("%d ", fa[i] ?: -1); }