NOIP2019普及组T4——加工零件

一道挺好的思维题。

题意简述

题目链接

  给定一张边权均为1无向图,共Q次询问,每次给出两个参数a,L,询问1号点到a号点之间是否存在长度为L的路径,其中边和点可以经过多次。

算法概述

  将1号点到每个点之间的路径长度设为dis,则对于每个询问,若L<min(dis[a]),显然无解。

  当L>=min(dis[a])时,若存在dis[a]与L奇偶性相同,则必然有解,否则无解。

  故,我们可先预处理出1号点到每个点的奇最短路和偶最短路,然后根据L的奇偶性,判断L是否大于与其奇偶性相同的最短路的长度即可。

  证明:

  先证充分性。

  简记dis[a]为d,由于d与L奇偶性相同,不妨设L=d+2*k(k∈N),那么对于1号点和a号点,必然可以找到与这两点中某一点u有直接边相连的一个点x,则可从u到x进行来回跑动,每次总距离会增加2,共跑k次,然后加上d,即可得到L的长度。充分性成立。

  必要性可用反证法同理得证。

  时间复杂度O(mlogn)

参考代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
const int N=1e5+10;
struct Edge{
	int to,next,w;
}edge[N<<1];int idx;
int h[N];

int vis[N][2],dis[N][2];
int n,m,q;

void add_edge(int u,int v,int w){edge[++idx]={v,h[u],w};h[u]=idx;}

void dijkstra()
{
	memset(vis,0,sizeof vis);
	memset(dis,0x3f,sizeof dis);
	
	priority_queue<pair<int,PII> > q;
	dis[1][0]=0;
	q.push(make_pair(0,make_pair(1,0)));
	
	while(!q.empty())
	{
		pair<int,int> p=q.top().y;
		q.pop();
		if(vis[p.x][p.y])continue;
		vis[p.x][p.y]=1;
		for(int i=h[p.x];~i;i=edge[i].next)
		{
			int to=edge[i].to;
			if(dis[to][0]>dis[p.x][1]+1)
			{
				dis[to][0]=dis[p.x][1]+1;
				q.push(make_pair(-dis[to][0],make_pair(to,0)));
			}
			if(dis[to][1]>dis[p.x][0]+1)
			{
				dis[to][1]=dis[p.x][0]+1;
				q.push(make_pair(-dis[to][1],make_pair(to,1)));
			}
		}
	}
}

int main()
{
	memset(h,-1,sizeof h);
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=m;i++)
	{
		int u,v;scanf("%d%d",&u,&v);
		add_edge(u,v,1);
		add_edge(v,u,1);
	}

	dijkstra();
	
	while(q--)
	{
		int a,l;scanf("%d%d",&a,&l);
		if(l%2)
		{
			if(l>=dis[a][1])printf("Yes\n");
			else printf("No\n");
		}
		else
		{
			if(l>=dis[a][0])printf("Yes\n");
			else printf("No\n");
		}
	}
	return 0;
}

  

posted @ 2020-08-05 08:31  魑吻丶殇之玖梦  阅读(383)  评论(0编辑  收藏  举报