【PER #1】捉迷藏 / Ptz2022 Day1.Kyoto U L 题解

今天心血来潮想改一改 pj 的题,发现了这场 easy round 的 A 还没改……


跟自己和解了,想了两天没想明白,说说大致思路。

题目链接

只考虑一组询问怎么做,先把 \(v\) 当作根,称那些始终满足距离条件的点就做“备选点”。

首先第一步 \(B\) 肯定要选定一个儿子走下去,然后根据获得的距离信息再做抉择。假设现在走到了 \(u\),如果“备选点”还在 \(u\) 的子树内,那么跟第一步一样,还是需要选定一个儿子走下去。

所以最坏情况下,\(B\) 会往下走 \(h=\lfloor \frac{d-1}{2} \rfloor\) 步,此时再往下走就寄了。

同时,\(B\) 在“备选点”还在子树内的时候,不会在中途往回走。因为最终时间取决于 \(A\) 追上来的时间,\(B\) 只要确定终点,无论中途怎么走,\(A\) 都会朝 \(B\) 即终点方向走,除非 \(B\) 玩脱了让 \(A\) 堵住了通往终点的路。

所以现在相当于要确定一个从 \(v\) 往下长度为 \(h\) 的路径,当“备选点”还在 \(u\) 子树内时让 \(u\) 沿这条路径走。

当然,除了上面的情况,还有可能 \(B\) 走着走着发现“备选点”不在下面了,此时 \(B\) 有两种抉择:

  • 铁了心往下面走

  • 赶紧回头碰碰运气,看能不能在 \(A\) 追上来前走一条更长的路。

实际上第二种走法非常劣,如果选择了回头走更长的路,那还不如一开始就走这条更长的路,这样更不容易被 \(A\) 抓到,反正用时也一样(与前面分析不会往回走一样)。所以一旦 \(B\) 发现“备选点”不在下面了,就会往子树内最深的叶子走。

所以如上分析,\(B\) 一开始就会朝着最远点走,且一定会走 \(h\) 步。

假设 \(B\)\(h\) 步后停在了 \(x\),现在,我们只需要分析最劣的那种情况————“备选点”在 \(x\) 子树内,然后算出答案。

(为什么是最劣的?因为上面说了“备选点”不在 \(x\) 子树内的话,\(B\) 就会往最远点走,而 \(A\) 当然不想便宜 \(B\),所以肯定会堵住这条路,即在 \(x\) 子树内)

这时 \(B\) 要么朝着没有“备选点”的儿子走,要么往上走。考虑怎么快速计算。

首先 \(v\) 的最远点一定是树的直径端点之一,可以快速计算。将 \(v\) 的最远点当作根,可以用倍增快速将 \(v\) 跳到 \(x\)。接着 \(x\) 的父亲肯定不能走(因为是最远点,上面一定有“备选点”),只需要考虑 \(x\) 的子树就行了。

\(x\) 是从 \(y\) 跳上来的,那么第二种情况相当于求 \(y\) 子树内的最深叶子,可以快速维护。

至于第一种情况,没有“备选点”的儿子相当于其最深叶子深度 \(< d-h-1\),可以用 vector 或 set 记录 \(x\) 每个儿子子树的最深叶子深度,排序后二分即可。

时间复杂度 \(\mathcal O(n\log n)\)

代码

#include<bits/stdc++.h>
using namespace std;
using namespace my_std;
ll n,q,head[100010],cnt=0,dep[100010],rt;
struct node{
	ll nxt,to;
}e[200020];
void add(ll u,ll v){
	e[++cnt].nxt=head[u];
	e[cnt].to=v;
	head[u]=cnt;
}
struct tree{
	vector<ll> vec[100010];
	ll rt,dep[100010],maxdep[100010],f[100010][22];
	void dfs(ll fa,ll u){
		dep[u]=dep[fa]+1;
		f[u][0]=fa;
		fr(i,1,18) f[u][i]=f[f[u][i-1]][i-1];
		go(u){
			ll v=e[i].to;
			if(v==fa) continue;
			dfs(u,v);
			maxdep[u]=max(maxdep[u],maxdep[v]+1);
			vec[u].push_back(maxdep[v]+1);
		}
		sort(vec[u].begin(),vec[u].end());
	}
	void init(ll RT){
		rt=RT;
		dfs(0,rt);
	}
	ll jump(ll x,ll t){
		pfr(i,18,0) if(t&(1ll<<i)) x=f[x][i];
		return x;
	}
	ll query(ll x,ll d){
		ll h=(d-1)/2,ans=0;
		if(h){
			x=jump(x,h-1);
			ans=max(ans,d-h+maxdep[x]+1);
			x=f[x][0];
		}
		ll tmp=lower_bound(vec[x].begin(),vec[x].end(),d-h)-vec[x].begin()-1;
		if(tmp>=0) ans=max(ans,d-h+vec[x][tmp]);
		else ans=max(ans,d-h);
		return ans;
	}
}t1,t2;
void dfs(ll fa,ll u){
	dep[u]=dep[fa]+1;
	if(dep[u]>dep[rt]) rt=u;
	go(u){
		ll v=e[i].to;
		if(v==fa) continue;
		dfs(u,v);
	}
}
int main(){
	n=read();
	q=read();
	fr(i,2,n){
		ll u=read(),v=read();
		add(u,v);
		add(v,u);
	}
	dfs(0,1);
	ll nrt=rt;
	rt=0; 
	t1.init(nrt);
	dfs(0,nrt);
	t2.init(rt);
	while(q--){
		ll v=read(),d=read();
		if(t1.dep[v]>=t2.dep[v]) writeln(t1.query(v,d));
		else writeln(t2.query(v,d));
	}
}

实际上本文有些地方不严谨,偏向感性(

主要是想了几天还是不会严谨证明,就摆烂了。有严谨证明的可以在下面回复,十分感谢!

posted @ 2023-02-01 22:17  AFewSuns  阅读(47)  评论(0编辑  收藏  举报