LOJ #6669 Nauuo and Binary Tree (交互题、树链剖分)

题目链接

https://loj.ac/problem/6669

题解

Orz yyf太神了,出这种又有意思又有意义的好题造福人类……
首先(n)次询问求出所有节点的深度。
考虑按深度扩展(BFS), 同时维护重链剖分
每次扩展一个点时,从根节点所在重链开始,每次询问当前节点与链底节点的距离,这样就可以算出它们LCA的深度,也就是当前节点到根的路径上与这条重链相交部分的最大深度。那么如果这个最大深度等于当前深度(-1), 就得到了该点的父亲;否则跳到LCA的轻儿子所在重链上,继续询问即可。
时间复杂度(O(n^2)), 询问次数(O(nlog n)).

代码

比我想象中的好写一些。

#include<bits/stdc++.h>
#define llong long long
using namespace std;

inline int read()
{
	int x = 0,f = 1; char ch = getchar();
	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
	return x*f;
}

const int N = 3000;
int son[N+3][2];
int hvs[N+3];
int dep[N+3];
int fa[N+3];
int sz[N+3];
vector<int> idep[N+3];
int n;

int getbtn(int u) {while(son[u][hvs[u]]) {u = son[u][hvs[u]];} return u;}
int jumpup(int u,int x) {while(x--) {u = fa[u];} return u;}
int jumpdown(int u,int x) {while(x--) {u = son[u][hvs[u]];} return u;}
void verify(int u) {hvs[u] = (sz[son[u][1]]>sz[son[u][0]]);}
void setfa(int u,int v) //fa[u]=v
{
//	printf("setfa %d %d
",u,v);
	fa[u] = v; sz[u] = 1; son[v][son[v][0]?1:0] = u;
	while(v)
	{
		verify(v); sz[v]++; v = fa[v];
	}
}

void work(int u)
{
	int v = getbtn(1);
	while(1)
	{
		printf("? %d %d
",v,u); fflush(stdout);
		int dis; scanf("%d",&dis);
		int deplca = (dep[u]+dep[v]-dis)>>1;
		v = jumpup(v,dep[v]-deplca);
		if(dep[u]-deplca==1) {setfa(u,v); return;}
		v = son[v][hvs[v]^1]; v = getbtn(v);
	}
}

int main()
{
	scanf("%d",&n);
	for(int i=2; i<=n; i++) {printf("? %d %d
",1,i); fflush(stdout); scanf("%d",&dep[i]); idep[dep[i]].push_back(i);}
	for(int i=0; i<idep[1].size(); i++) {setfa(idep[1][i],1);}
	for(int i=2; i<=n; i++)
	{
		for(int j=0; j<idep[i].size(); j++)
		{
			int u = idep[i][j];
			work(u);
		}
	}
	printf("! "); for(int i=2; i<=n; i++) printf("%d ",fa[i]); puts(""); fflush(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/suncongbo/p/12070871.html