CF2013 F2

CF2013 F2

首先你需要知道 F1 的做法。

我将会给出一个 O(nn) 的,求出整棵树任意节点答案的方法。

对于路径上的点 p1pm,终点 pm,起点 p1

pi 所经不在路径上的最远长度为 di

那么根据 F1 的结论,我们是通过移动两个指针 l,r,不断判断是否有 dl+l>maxk[l,r](mk+1+dk)dr+mr+1maxk[l,r]dk+k

在这里我们设 ai=di+i,bi=dii+1,然后利用 ST 表维护最值进行判断。

事实上我们观察 i 其实是深度,与此同时如果 pipi+1 不是在 pi 的树内最长路上的话,ai 就是 a 的一个后缀最大值。反过来 b 可以从 pm 为根的视角来看。

所以我们尝试在 ST 表中维护最值出现的位置,然后维护指针 l=1,r=m,每次看谁移动得更少,就用谁进行判断,如果合法直接 return,否则跳到后继位置(下一个最值位置,注意值相同的跳到最后面去),当然反过来是同理的。

由于我们每一次记录了最值,所以不会影响判断结果的完备性。

而同时我们发现 l,r 指针所跳都是前/后缀最大值,而这种最大值都是各个长链的链顶提供的,所以总共 O(n) 个,所以我们暴力检验就是正确的。

同时我们可以用尾插ST表,维护递归结构。

#include<bits/stdc++.h>
using namespace std;
#define N 205050
int lg[N],n,m,dep[N],ans[N],mx[N],lst[N];
struct ST{
	int f[N][20],v[N],tag;//尾插ST表hh
	void ins(int x,int k){
		f[x][0]=x,v[x]=k;
		for(int j=1;(1<<j)<=x;++j){
			int lt=x-(1<<j-1);
			f[x][j]=v[f[x][j-1]]>v[f[lt][j-1]]+tag?f[x][j-1]:f[lt][j-1];//维护最前面的/最后面的最值,tag=0说明是取最前的,tag=-1说明是取最后面的
		}
	}
	int ask(int l,int r){
		int k=lg[r-l+1];l+=(1<<k)-1;
		return v[f[r][k]]>v[f[l][k]]+tag?f[r][k]:f[l][k];
	}
}A,B;
vector<int>e[N];
void dfs(int u,int fa){
	lst[u]=fa;dep[u]=dep[fa]+1;
	for(auto v:e[u]){
		if(v==fa)continue;
		dfs(v,u);
		mx[u]=max(mx[u],mx[v]+1);
	}
}
void adt(int dep,int val){
	A.ins(dep,val+dep);
	B.ins(dep,val-dep+1);
}
bool check(int dep){
	if(dep<=1)return 1;
	int mid=dep+1>>1;
	int l=A.ask(1,mid),r=B.ask(mid+1,dep);
	while(1){//长剖,复杂度最多根号!
		if(l-1<=dep-r){
			if(A.v[l]>B.v[B.ask(l+1,dep-l+1)]+dep)return 1;
			if(l>=mid)return 0;
			l=A.ask(l+1,mid);
		}
		else {
			if(B.v[r]+dep>=A.v[A.ask(dep-r+2,r-1)])return 0;
			if(r<=mid+1)return 1;
			r=B.ask(mid+1,r-1);
		}
	}
	return 0;
}
void dfs2(int u,int fa){
	int mxx=0,cmx=0;
	for(auto v:e[u])if(v!=fa){
		if(mx[v]+1>=mxx)cmx=mxx,mxx=mx[v]+1;
		else cmx=max(cmx,mx[v]+1);
	}
	adt(dep[u],mxx);
	ans[u]=check(dep[u]);
	for(auto v:e[u])if(v!=fa){
		adt(dep[u],mx[v]+1==mxx?cmx:mxx);
		dfs2(v,u);
	}
}
void sol(){
	cin>>n;
	for(int i=1,u,v;i<n;++i)cin>>u>>v,e[u].push_back(v),e[v].push_back(u);
	dfs(1,0);
	dfs2(1,0);
	int u,v;cin>>u>>v;
	vector<int>nl,nr;
	while(u!=v){
		if(dep[u]>dep[v])nl.push_back(u),u=lst[u];
		else nr.push_back(v),v=lst[v];
	}
	reverse(nr.begin(),nr.end());
	nl.push_back(u);
	for(auto x:nl)cout<<(ans[x]?"Alice\n":"Bob\n");
	for(auto x:nr)cout<<(ans[x]?"Alice\n":"Bob\n");
	for(int i=1;i<=n;++i)mx[i]=lst[i]=0,e[i].clear();
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	B.tag=-1;
	lg[0]=-1;for(int i=1;i<=200000;++i)lg[i]=lg[i>>1]+1;
	int T;cin>>T;
	while(T--){
		sol();
	}
}
posted @   spdarkle  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示