1. 程式人生 > 其它 >CF772E Verifying Kingdom【互動,點分治】

CF772E Verifying Kingdom【互動,點分治】

給定正整數 \(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\)

,先將當前得到的樹點分,每次找到當前連通塊的重心 \(c\),若 \(c\) 是葉子那麼建新點連向 \(c,i\),若 \(c\) 不是葉子則在左右子樹中分別找葉子 \(l,r\),詢問 \(l,r,i\) 就可以知道 \(i\)\(c\) 的左/右子樹還是 \(c\) 的子樹外。遞迴到對應位置,如果沒地方加了就在當前邊上建新點連向 \(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);
}