CF954D Fight Against Traffic C++题解

洛谷题目传送门

题意

给定一个 \(n\) 个点,\(m\) 条边的无向联通图。你现在可以随意将两个没有连起来的点相连(只能连一条),输出有多少种连法不影响点 \(s\) 到点 \(t\) 的最短路径长度。

点的编号由 \(1\)\(n\),且边权均为 \(1\)

思路

要从 \(s\)\(t\),若会影响最小值,肯定经过了刚才连上的一条边。
现称:

  • \(d1_i\) 表示从 \(s \to i\) 的最短路长度;
  • \(d2_i\) 表示从 \(i \to t\) 的最短路长度,同时因为这是无向图,故 \(d2_i\) 的含义 \(\Leftrightarrow t \to i\) 的最短路长度;
  • 连上的边是 \(x\)\(y\) 这条边。

则,从 \(s\)\(t\) 有两种情况:

  1. \(s \to x \to y \to t\)
  2. \(s \to y \to x \to t\)

由于 \(x \to y\) 的权是 \(1\)
则两种情况所对应的距离分别是:

  1. \(ans1 = d1_x + 1 + d2_y\)
  2. \(ans2 = d1_y + 1 + d2_x\)

由于 \(s \to t\) 的最短路的长度是 \(d1_t\) \((d1_t = d2_s\),都是这个最短的长度\()\)

如果 \(\min(x1, x2) < d1_t\),说明影响了,我们要求不影响的条数,故

  • 如果 \(\min(x1, x2) \geqslant d1_t\)cnt++

本题的大体思路总结:

  1. 预处理 \(d1\)\(d2\)
  2. \(O(n^2)\) 枚举每一对 \(x\)\(y\),如果 \(x \ne y \land x\)\(y\) 之间没有连边,求出 \(ans1\)\(ans2\),并判断是否合法;
  3. 输出答案。

下面简单说一下预处理 \(d1\)\(d2\)
本题没有重边,故可以邻接矩阵来存图。
直接用 bfs 算出所有点的最短路。

  • \(d1\) 时,队列中先插入 \(s\),接下来扩展到下一个点;
  • \(d2\) 时,队列中先插入 \(t\),接下来同上。

解释一下第三个最简单的样例:

如图,从 \(1 \to 5\),原图的最短路长度为 \(2\), 有以下 \(3\) 中连法使得最短路长度不变:

  1. 连接 \(2 \to 3\),最短路长度不变。


2. 连接 \(2 \to 4\),最短路长度不变。


3. 连接 \(3 \to 4\),最短路长度不变。

故输出 \(3\)

代码

AC code记录

#include <bits/stdc++.h>
#define fr(i, l, r) for (int i = l; i <= r; ++i)
using namespace std;
int n, m, s, t, x, y, cnt, d1[1010], d2[1010], link[1010][1010];
bool vis[1010];
queue<int>q;

int min(int x, int y)
{
	return x < y ? x : y;
}

void bfs(int *dis)
{
	while (q.size())
	{
		int now = q.front(); q.pop();
		fr(i, 1, n)
		{
			if (!link[now][i] || vis[i]) continue;
			vis[i] = true, dis[i] = dis[now] + 1, q.push(i);
		}
	}
	fr(i, 1, n) vis[i] = false; // 清空数组 
	while (q.size()) q.pop();
}

int main()
{
	scanf("%d%d%d%d", &n, &m, &s, &t);
	fr(i, 1, m) scanf("%d%d", &x, &y), link[x][y] = 1, link[y][x] = 1;
	vis[s] = true, /*这里数组标 true 不能忘*/q.push(s), bfs(d1);
	vis[t] = true, q.push(t), bfs(d2);
	fr(i, 1, n) fr(j, i, n)
	{
		if (i == j || link[i][j]) continue; // 如果本来就有边或者 i = j,continue 
		int ans1 = d1[i] + 1 + d2[j],
			ans2 = d1[j] + 1 + d2[i];
		cnt += min(ans1, ans2) >= d1[t]; // 如果两种方案的最小值大于等于原本的最短路的长度,说明合法 
	}
	printf("%d", cnt);
	return 0;
}
}
posted @ 2021-06-18 18:15  StayAlone_lrc  阅读(129)  评论(0)    收藏  举报