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 
#include 
#include 
#include  
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 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]=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< 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=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");
	}
}