[GDKOI2023 提高组] 游戏 题解

一种比较简短的写法:

拉出直径,再在直径的每一个点上跑一下最长链,为 $ mx_i$

这里设三点的路径交点为 \(rt\)

假设 \(rt \rightarrow u,v,w\) 的距离为 \(dis1,dis2,dis3\)

容易知道 \(dis1 = (x+y-z)/2,dis2 = (-x+y+z)/2,dis3 = (x-y+z)/2\)

在直径上的 \([dis1+1,len-dis2]\) 中选择 \(mx\) 最大值判断是否大于等于 \(dis3\)

若成立,则在 \([dis2+1,len-dis1]\) 中选择 \(mx\) 最大值判断是否大于等于 \(dis3\)

明显这样做是在 \(dis1\ge dis2\ge dis3\) 时成立,若 \(dis1,dis2,dis3\) 不满足递减关系,先排序,最后调整关系即可。

时间复杂度 \(O(n \log n +q)\),常熟较小,比次优解快一半。

放一个未卡常的个人觉得比较清新的代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2E5+5;
int n,q,dep[N],rt,fa[N],st[N][21],mx[N],lg[N],vis[N],a[4],b[4];
vector<int>e[N],g,h[N];
int get(int x,int y){
	return mx[x]>mx[y]?x:y;
}
void dfs(int u,int F){
	fa[u]=F;
	dep[u]=dep[F]+1;
	if(dep[u]>dep[rt])rt=u;
	for(int v:e[u])if(v!=F&&!vis[v])dfs(v,u);
}
int query(int l,int r){
	int tmp=lg[r-l+1];
	return get(st[l][tmp],st[r+1-(1<<tmp)][tmp]);
}
bool cmp(int x,int y){
	return a[x]>a[y];
}
int main(){
	freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
	scanf("%d",&n);
	for(int i=1,u,v;i<n;i++){
		scanf("%d%d",&u,&v);
		e[u].push_back(v),e[v].push_back(u);
	}
	dfs(1,0);
	dfs(rt,0);
	while(rt)vis[rt]=1,g.push_back(rt),rt=fa[rt];
	for(int i=0;i<g.size();i++){
		int x=g[i];
		rt=0;
		dep[0]=-1;
		st[i+1][0]=i;
		dfs(x,0);
		mx[i]=dep[rt];
		while(rt)h[i].push_back(rt),rt=fa[rt];
		reverse(h[i].begin(),h[i].end());
	}
	for(int i=1;i<=20;i++)
		for(int j=1;j+(1<<i-1)<=g.size();j++)
			st[j][i] = get(st[j][i-1],st[j+(1<<i-1)][i-1]);
	for(int i=2;i<=n;i++)
		lg[i]=lg[i>>1]+1;
	scanf("%d",&q);
	while(q--){
		int u,v,w;
		for(int i=1;i<=3;i++)
			scanf("%d",a+i),b[i]=i;
		sort(b+1,b+4,cmp);
		int tmp1= (a[b[1]] + a[b[2]] - a[b[3]])/2;
		int tmp2= a[b[1]] - tmp1;
		int tmp3= a[b[2]] - tmp1;
		int tmp= query(tmp1+1,g.size()-tmp2);
		if(mx[tmp] >= tmp3){
			u = g[tmp-tmp1];
			v = g[tmp+tmp2];
			w = h[tmp][tmp3];
		} 
		else {
			tmp= query(tmp2+1,g.size()-tmp1);
			v = g[tmp-tmp2];
			u = g[tmp+tmp1];
			w = h[tmp][tmp3];
		}
		if (b[1] == 1 && b[2] == 2)
			printf("%d %d %d\n", u, v, w);
		if (b[1] == 1 && b[2] == 3)
			printf("%d %d %d\n", v, u, w);
		if (b[1] == 2 && b[2] == 1)
			printf("%d %d %d\n", u, w, v);
		if (b[1] == 2 && b[2] == 3)
			printf("%d %d %d\n", v, w, u);
		if (b[1] == 3 && b[2] == 1)
			printf("%d %d %d\n", w, u, v);
		if (b[1] == 3 && b[2] == 2)
			printf("%d %d %d\n", w, v, u);
	}
	return 0;
	
}
posted @ 2025-09-19 22:05  hnczy  阅读(8)  评论(0)    收藏  举报