[題解]UVA10917 Walk Through the Forest
阿新 • • 發佈:2021-07-08
UVA10917 Walk Through the Forest 題解
#1.0 理解題意
本題最需要理解的是這句話:
如果有一條從B到他的家的路線比從A到他的家的任何可能的路線都短,那麼他認為從A到B更好。
設 \(d_x\) 表示 \(x\) 到 \(2\) 號節點的最短路,那麼上文的意思就是如果 \(A,B\) 間存在道路且 \(d_B<d_A\),那麼道路 \(A\to B\) 便是一種可行的選擇。
#2.0 大體思路
根據上面那句話,我們便需要先處理出所有點到 \(2\) 號點的最短距離。然後按照上面的要求重新建圖,這裡建出的邊是單向邊,再注意到得到的圖是一張有向無環圖(\(\texttt{DAG}\)),因為每條邊的邊權都是非負數,用反證法不難證明得到的最短路圖不存在環。
此時考慮 \(\texttt{DAG}\) 上的動態規劃,設 \(sum_x\) 表示從 \(1\) 號節點到 \(x\) 號節點可行的路徑條數,顯然有
\[sum_x=\sum\limits_{(y,x)\in E}sum_y. \]這裡 \(E\) 表示邊集,有序數對 \((u,v)\) 表示一條從 \(u\) 的 \(v\) 的有向邊。邊界為 \(sum_1=1.\)
同時注意到,節點 \(1\) 不一定是入度為 \(0\) 的點,入度為零的點除 \(1\) 外不會對答案造成貢獻。
拓撲結束後得到的結果便是本題所求。
#define pii pair<int, int> #define mp(a, b) make_pair(a, b) #define mset(l, x) memset(l, x, sizeof(l)) const int N = 100010; const int INF = 0x7fffffff; struct Edge { int u, v, w; int nxt; }; Edge e[N], ne[N]; int cnt = 1, ncnt = 1, head[N], nhead[N]; int dist[N], vis[N], tot[N], s, t, icnt[N]; int qx[N], frt, tal, tag[N], n, m; priority_queue <pii > q; inline void add(const int &u, const int &v, const int &w) { e[cnt].u = u, e[cnt].v = v, e[cnt].w = w; e[cnt].nxt = head[u], head[u] = cnt ++; } inline void addn(const int &u, const int &v) { ne[ncnt].u = u, ne[ncnt].v = v, icnt[v] ++; ne[ncnt].nxt = nhead[u], nhead[u] = ncnt ++; } void Dijkstra() { mset(dist, 0x3f); q.push(mp(0,2)); dist[2] = 0; while (q.size()) { int now = q.top().second; q.pop(); if (vis[now]) continue; vis[now] = true; for (int i = head[now]; i; i = e[i].nxt) if (dist[e[i].v] > dist[now] + e[i].w) { dist[e[i].v] = dist[now] + e[i].w; q.push(mp(-dist[e[i].v], e[i].v)); } } } inline void clear() { frt = 0, tal = -1; ncnt = cnt = 1; mset(tot, 0); mset(icnt, 0); mset(tag, 0); mset(vis, 0); mset(dist, 0x3f); mset(head, 0); mset(nhead, 0); } void topo() { mset(vis, 0); for (int i = 1; i <= n; i ++) if (!icnt[i] || i == 1) qx[++ tal] = i; tag[1] = tot[1] = vis[1] = 1; while (frt <= tal) { int now = qx[frt ++]; for (int i = nhead[now]; i;i = ne[i].nxt) { icnt[ne[i].v] --; if (tag[now]) { tot[ne[i].v] += tot[now], tag[ne[i].v] |= tag[now]; } if (!icnt[ne[i].v] && !vis[ne[i].v]) { qx[++ tal] = ne[i].v; vis[ne[i].v] == true; } } } } int main() { scanf("%d", &n); while (n) { scanf("%d", &m); s = 1, t = 2; clear(); for (int i = 1; i <= m; i ++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); add(u, v, w); add(v, u, w); } Dijkstra(); for (int i = 1; i <= n; i ++) for (int j = head[i]; j; j = e[j].nxt) if (dist[i] > dist[e[j].v]) addn(i, e[j].v); topo(); printf("%d\n", tot[t]); scanf("%d", &n); } return 0; }
End
希望能為各位帶來幫助。