多校訓練第1輪.A——À la Volonté du Peuple【最短路】

題目傳送門


題目背景

Please note that we changed the sample input 1 a little bit to fit the illustration. The answer for either version is correct though. PDF updated. Sorry for troubles caused.


題目描述

Will you give all you can give
So that our banner may advance?
Some will fall and some will live
Will you stand up and take your chance?
The blood of the martyrs
Will water the meadows of France!

A solitary spark can start a prairie fire.

You are given a weighted connected undirected graph with nn vertices and mm edges. You know that someone light up a fire at vertex 11, which will burn the current position into dust immediately and expand to adjacent places at the speed 11 mile per second. The fire will split at the vertices to all those edges who have not been lighten up, and will cause a blast when at least two fires meet at the same point.

Revolutionaries love explosions. They want you to count the number of explosions that will happen on the graph.

在這裏插入圖片描述


輸入格式

The first line contains integers n,m(1n3×105,0m106)n,m (1 \leq n \leq 3 \times 10 ^ 5, 0 \leq m \leq 10 ^ 6) - the number of vertices.

Each of the next mm lines contains 33 integers ui,vi,wi(1ui,vin,1wi9)u_i, v_i, w_i (1 \leq u_i, v_i \leq n, 1 \leq w_i \leq 9) - the ends of the i-thi\text{-th} edge and the weight of the i-thi\text{-th} edge.

It’s guaranteed that the graph is connected.

The graph may contain self loops and multiple edges. Example 11 shows the method to deal with them.

The size of input file may be large. Please, do not read input by too slow ways.


輸出格式

Output the number of explosions that will happen on the graph in a single line.

輸入

2 3
1 1 1
1 2 1
1 2 1

4 5
1 2 1
1 3 1
2 3 1
2 4 1
3 4 1


輸出

2

2


說明/提示

Here’s the illustration for example 1:
在這裏插入圖片描述

Here’s the illustration for example 2:
在這裏插入圖片描述


題意

  • 給你一個 nn 個點的無向圖(允許自環)
  • 從編號爲 11 的點開始點火,然後沿着邊跑,可以分身(說白了就是火藥被點着了)
  • 當火藥被兩個以上不同方向同時被點燃的時候,會發生爆炸
  • 詢問能發生幾次爆炸
  • 1n3×105,   0m106,    1wi91 \leq n \leq 3 \times 10 ^ 5, \ \ \ 0 \leq m \leq 10 ^ 6,\ \ \ \ 1 \leq w_i \leq 9

題解

  • 火藥燃燒的過程其實就是在跑 最短路
  • 在點上爆炸意味着存在至少兩個點可以是他最短路上的前驅;
  • 在邊上爆炸意味着**這條邊不在最短路里。
  • 因此求出最短路即可。
  • 邊權 ≤ 9 並不意味着可以使用 SPFA。
  • 本題的本意是使用 O(nw)O(nw) 的桶排序 Dijkstra,Dijkstra, 但是由於 vectorvector 實現的桶排 DijkstraDijkstra 和鄰接表 + priorityqueuepriority_queue 實現的 O(mlogm)O(m log m) DijkstraDijkstra 速度在使用輸入隨機數生成器的方法開大數據後沒法拉開差距,因此爲了避免卡常數都放了過去。
  • 本題真的不卡常數,測出來時限不寬是因爲讀入太慢了。

AC-Code O2開O2優化

#include <bits/stdc++.h>
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

const int maxn = 3e5 + 7;
const int maxm = 1e6 + 7;
struct P {
	int to, val;
	P() { to = val = 0; }
	P(int a, int b) :to(a), val(b) {};
	bool operator < (const P& t)const { return val > t.val; }
};
vector<P> G[maxm];
int dis[maxn];
bool vis[maxn];
void add(int u, int v, int w) {
	G[u].push_back(P(v, w));
	if (u != v)	G[v].push_back(P(u, w));
}
void dijk() {
	memset(dis, 0x3f, sizeof dis);
	memset(vis, false, sizeof vis);
	priority_queue<P> q;
	q.push(P(1, 0));	dis[1] = 0;
	while (!q.empty()) {
		P tmp = q.top();	q.pop();
		if (vis[tmp.to])	continue;
		vis[tmp.to] = true;
		for (int i = 0; i < G[tmp.to].size(); ++i) {
			int pos = G[tmp.to][i].to;
			int value = G[tmp.to][i].val;
			if (dis[pos] > dis[tmp.to] + value) {
				dis[pos] = dis[tmp.to] + value;
				q.push(P(pos, dis[pos]));
			}
		}
	}
}
int main() {
	ios;
	int n, m;	while (cin >> n >> m) {
		for (int i = 1; i <= m; ++i) {
			int u, v, w;	cin >> u >> v >> w;
			add(u, v, w); // 無向圖
		}
		dijk(); // 跑一邊最短路
		int ans = 0;
		for (int i = 1; i <= n; ++i) {
			int cnt = 0;
			for (P v : G[i]) { // v:點i的鄰接點+權值,前驅、後繼。
				if (dis[i] == dis[v.to] + v.val) // 找交點爆炸
					++cnt; // v->i和dis[i]相同,v可以是最短路上的點
				else if (i >= v.to && dis[i] + v.val > dis[v.to] && dis[v.to] + v.val > dis[i])	   ++ans;
			}
			if (cnt > 1)	++ans; // 倆以上前驅等距離,在交點爆炸1次
		}
		cout << ans << endl;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章