欢迎来爆踩我~|

AFewSuns

园龄:4年11个月粉丝:42关注:3

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

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


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

题目链接

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

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

所以最坏情况下,B 会往下走 h=d12 步,此时再往下走就寄了。

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

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

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

  • 铁了心往下面走

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

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

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

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

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

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

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

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

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

时间复杂度 O(nlogn)

代码

#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));
	}
}

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

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

本文作者:AFewSuns

本文链接:https://www.cnblogs.com/AFewSuns/p/17081101.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   AFewSuns  阅读(53)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起