1. 程式人生 > 其它 >CF954D Fight Against Traffic C++題解

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;
}
}