zoukankan      html  css  js  c++  java
  • 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");
    	}
    }
    
  • 相关阅读:
    缺少环境变量导致Xilinx Platform Studio无法打开之解决方法
    PS利用EMIO控制LED灯
    利用zedboard添加自定义IP核完成简易计算器
    读后感 关于 《不要一辈子靠技术混饭吃》(挪窝第一篇)
    挪窝了,再谈blog
    windows server 2008 无法打开角色,角色错误,刷新服务器时出现意外错误,HRESULT:0x80070422
    Linux 基础正则表达式和扩展正则表达式
    Linux 基础正则表达式
    Linux 通配符与特殊符号
    Linux 基础命令
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15763162.html
Copyright © 2011-2022 走看看