一道挺好的思维题。
题意简述
给定一张边权均为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 "); else printf("No "); } else { if(l>=dis[a][0])printf("Yes "); else printf("No "); } } return 0; }