『题解』Luogu P3556 [POI2013]MOR-Tales of seafaring

\(\textsf{update on 2022/6/5}\) 修改了一下代码。


题目传送门

题目大意

\(n\) 个点 \(m\) 条边无向图,每次询问两个点之间是否有长度为 \(d\) 的路径(不一定是简单路径)。

思路

使用 bfs 广搜。

题目求的路径长度 \(d\) 不一定为简单路径,意为可以重复走边。

可以重复走边,那么我们只需求出每个点的长度为奇数的最短路径和长度为偶数的最短路径。

为什么这么说呢?观察重复走边,就是你回去一次,再跑过来一次,那么每次要加上 \(2\) 长度。那么只需判断 \(d\) 是否比最短路径大且与最短路长度奇偶性相同。

如何求出奇数最短路和偶数最短路呢?利用拆点,将每个点拆成两个,分别表示到这个点且路径长度为奇数和偶数的路径长度。

广搜时从每个偶数点开始搜索,建立 dis,查询时可 \(\mathcal{O}(1)\) 复杂度。

查询时,奇数和偶数情况都判断一下,如果 \(d\) 为奇数,就去奇数最短路径表中查询,如果奇数情况下的最短路径有,并且小于或等于 \(d\) 那么就有长度为 \(d\) 的路径。反之亦然。

代码

#include <iostream>
#include <queue>
using namespace std;
template<typename T=int>
inline T read(){
    T X=0; bool flag=1; char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-') flag=0; ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<1)+(X<<3)+ch-'0',ch=getchar();
    if(flag) return X;
    return ~(X-1);
}

template<typename T=int>
inline void write(T X){
    if(X<0) putchar('-'),X=~(X-1);
    T s[20],top=0;
    while(X) s[++top]=X%10,X/=10;
    if(!top) s[++top]=0;
    while(top) putchar(s[top--]+'0');
    putchar('\n');
}

const int N=2e4+5,M=2e5+5;
struct edge{ // 链式前向星存图
	int to,next;
}e[M];
int n,m,k,u,v,d;
int head[N],top;
int dis[N][N]; // dis[i][j]:i->j的最短路径长度
queue<int> q; // bfs所用队列

void add(int u,int v){ // 加边不多说
	top++;
	e[top].to=v;
	e[top].next=head[u];
	head[u]=top;
}

void bfs(int id){ // id 表示搜索的点的编号
	// 广搜差不多是模板
	q.push(id);
	while(!q.empty()){
		u=q.front();
		q.pop();
		for(int i=head[u]; i; i=e[i].next){ // 遍历,更新最短路径
			v=e[i].to;
			if(!dis[id][v]){ // 只 bfs 一次,首先被遍历的才是最短的
				dis[id][v]=dis[id][u]+1; // 因为边权始终为 1,所以更新一次就行。
				q.push(v);
			}
		}
	}
}

int main(){
	n=read(),m=read(),k=read();
	while(m--){
		u=read(),v=read();
		add(u+n,v),add(v,u+n); // 拆点&建边(双向)
		add(u,v+n),add(v+n,u);
	}
	for(int i=1; i<=n; i++) bfs(i); // 预处理 distance
	while(k--){
		u=read(),v=read(),d=read();
		if(d&1 && dis[u][v+n]<=d && dis[u][v+n] // 奇数情况:查询u->v的奇数最短路
		|| !(d&1) && dis[u][v]<=d && dis[u][v]) // 偶数情况:查询u->v的偶数最短路
			puts("TAK"); // 如果最短路径小于等于要求路径,且两种情况满足任意一个,就OK
		else puts("NIE"); // 否则输出 NIE
	}
	return 0;
}
posted @ 2022-01-21 21:19  仙山有茗  阅读(74)  评论(0编辑  收藏  举报