CF1307F Cow and Vacation

一、题目

点此看题

二、解法

这道题又是我自己想出来的,但是好像 \(3300\) 的评分有点虚高了吧。

其实本题就是问的一个可达性,那么我们可以考虑往连通性上思考。首先考虑本题是否是双向联通的,也就是从 \(a\) 出发能到达 \(b\),那么从 \(b\) 出发就能到达 \(a\),这个性质不难证明。

但是考察连通性有一个问题,也就是所谓“连通”的两点能到达的范围是不同的(因为起始步数的原因),如果我们想要利用连通的性质需要连通块上每个点是等价的(无论谁是出发点都可互达),然后我们发现如果只考虑关键点就正好契合这一点,因为只要到了关键点剩余步数就会重置为 \(k\)

那么我们考虑建出关键点的连通网络,那么查询就可以考虑从一个距离起点小于等于 \(m\) 的关键点上车,然后从一个距离终点小于等于 \(m\) 的关键点下车;或者是不用关键点直接走。

思路就是上面那些了,现在来说一下实现细节(这里参考了一下其他题解):

连通网络的建立可以考虑用 \(\tt bfs\) 和并查集,我们从每个关键点开始走 \(\frac{m}{2}\) 步,然后把这些范围内的点并查集合并。然后如果 \(m\) 是奇数就很不好判断,所以我们把边拆成点,那么距离都\(\times 2\),我们走 \(m\) 步即可。

对于询问我们可以从起点和终点都往 \(\tt lca\)\(m\) 步,然后看是否在一个并查集中,不难证明往上跳满是最优的(考虑我们 \(\tt bfs\) 的过程),所以总时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <vector>
#include <iostream>
#include <queue> 
using namespace std;
const int M = 400005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,q,p[M],d[M],dep[M],fa[M][20];
vector<int> g[M];
void dfs(int u,int p)
{
	fa[u][0]=p;
	dep[u]=dep[p]+1;
	for(int i=1;i<20;i++)
		fa[u][i]=fa[fa[u][i-1]][i-1];
	for(auto v:g[u]) if(v^p) dfs(v,u);
}
int lca(int u,int v)
{
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=19;i>=0;i--)
		if(dep[fa[u][i]]>=dep[v])
			u=fa[u][i];
	if(u==v) return u;
	for(int i=19;i>=0;i--)
		if(fa[u][i]^fa[v][i])
			u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
int jump(int x,int y)
{
	for(int i=19;i>=0;i--)
		if(y&(1<<i)) x=fa[x][i];
	return x;
}
int find(int x)
{
	if(x!=p[x]) p[x]=find(p[x]);
	return p[x];
}
void bfs()
{
	queue<int> q;
	for(int i=1;i<2*n;i++)
		p[i]=i,d[i]=-1;
	for(int i=1;i<=k;i++)
	{
		int u=read();
		d[u]=0;q.push(u);
	}
	while(!q.empty())
	{
		int u=q.front();q.pop();
		if(d[u]==m) break;
		for(int v:g[u])
		{
			p[find(v)]=find(u);
			if(d[v]==-1)
			{
				d[v]=d[u]+1;
				q.push(v);
			}
		}
	}
}
signed main()
{
	n=read();m=read();k=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		g[u].push_back(n+i);g[n+i].push_back(u);
		g[v].push_back(n+i);g[n+i].push_back(v);
	}
	dfs(1,0);bfs();
	q=read();
	while(q--)
	{
		int u=read(),v=read(),x=lca(u,v);
		int d=dep[u]+dep[v]-2*dep[x];
		if(d<=2*m) {puts("YES");continue;}
		int A=dep[u]-dep[x]>=m?find(jump(u,m)):find(jump(v,d-m));
		int B=dep[v]-dep[x]>=m?find(jump(v,m)):find(jump(u,d-m));
		if(A==B) puts("YES");
		else puts("NO");
	}
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15763162.html