LOJ2316. 「NOIP2017」逛公園【DP】【最短路】【思維】
阿新 • • 發佈:2018-11-01
思路
因為我想到的根本不是網上的普遍做法
所以常數出奇的大,而且做法極其暴力
可以形容是帶優化的大模擬
進入正題:
首先一個很顯然的思路是如果在合法的路徑網路裡面存在零環是有無陣列解的
然後這個直接對所有邊權是0的邊進行一次toposort看看有沒有點沒有被訪問到
然後剩下的dp怎麼設計?
\(dp_{i,j}\)表示走到了第i個點,如果當前點到n走最短路最後路徑比最短路徑多出來了j
然後轉移的時候發現是需要搞定順序的問題?咋辦?
發現一條邊新的貢獻是\(dis2_{v}+E[i].w-dis2_u\),其中dis2是反圖跑出來的最短路
然後就可以把每條邊的權值換一下
發現這個時候我們要先列舉j進行轉移
這樣就把狀態轉移分成了若干個層次
然後發現新的邊權如果是0的話會在同一層之間轉移
並且可以保證新的邊權是沒有0環的
那就再做一遍toposort就好了
隨便跑一跑多好的
然後同一層你就先跑所有新的權值是0的邊,同層轉移完了之後你再跑權值不是0的邊轉移到下一層就可以了
附帶Debug全套餐。。。良心對拍程式(懶得刪了)
//Author: dream_maker #include<bits/stdc++.h> using namespace std; //---------------------------------------------- //typename typedef long long ll; //convenient for #define fu(a, b, c) for (int a = b; a <= c; ++a) #define fd(a, b, c) for (int a = b; a >= c; --a) #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a) //inf of different typename const int INF_of_int = 1e9; const ll INF_of_ll = 1e18; //fast read and write template <typename T> void Read(T &x) { bool w = 1;x = 0; char c = getchar(); while (!isdigit(c) && c != '-') c = getchar(); if (c == '-') w = 0, c = getchar(); while (isdigit(c)) { x = (x<<1) + (x<<3) + c -'0'; c = getchar(); } if (!w) x = -x; } template <typename T> void Write(T x) { if (x < 0) { putchar('-'); x = -x; } if (x > 9) Write(x / 10); putchar(x % 10 + '0'); } //---------------------------------------------- //#define Debug typedef pair<int, int> pi; const int N = 2e5 + 10; const int M = 55; struct Edge { int u, v, w, nxt; bool dir, eras; } E[N << 1]; int head[N], tot = 0, vis[N]; int dis1[N], dis2[N], deg[N], topoid[N]; int n, m, k, p, minidis, dp[N][M]; void init() { tot = 0; fu(i, 1, n) head[i] = deg[i] = 0; fu(i, 1, n) fu(j, 0, k) dp[i][j] = 0; } void addedge(int u, int v, int w) { #ifdef Debug printf("EDGE:[%d, %d]: %d\n", u, v, w); #endif E[++tot] = (Edge) {u, v, w, head[u], 0, 0}, head[u] = tot; E[++tot] = (Edge) {v, u, w, head[v], 1, 0}, head[v] = tot; } void add(int &a, int b) { if ((a += b) >= p) a -= p; } void Dijkstra1() { static priority_queue<pi, vector<pi>, greater<pi> > q; fu(i, 1, n) dis1[i] = INF_of_int, vis[i] = 0; dis1[1] = 0; q.push(pi(dis1[1], 1)); while (q.size()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i; i = E[i].nxt) { int v = E[i].v; if (!E[i].dir && dis1[v] > dis1[u] + E[i].w) { dis1[v] = dis1[u] + E[i].w; q.push(pi(dis1[v], v)); } } } } void Dijkstra2() { static priority_queue<pi, vector<pi>, greater<pi> > q; fu(i, 1, n) dis2[i] = INF_of_int, vis[i] = 0; dis2[n] = 0; q.push(pi(dis2[n], n)); while (q.size()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i; i = E[i].nxt) { int v = E[i].v; if (E[i].dir && dis2[v] > dis2[u] + E[i].w) { dis2[v] = dis2[u] + E[i].w; q.push(pi(dis2[v], v)); } } } } bool toposort() { int ind = 0; fu(i, 1, n) vis[i] = 0; fu(i, 1, tot) { if (E[i].eras || E[i].w) continue; deg[E[i].v]++; } static queue<int> topo; fu(i, 1, n) if (!deg[i]) topo.push(i); while (topo.size()) { int u = topo.front(); topo.pop(); #ifdef Debug printf("Topotost :: Reach: %d\n", u); #endif topoid[++ind] = u, vis[u] = 1; for (int i = head[u]; i; i = E[i].nxt) { int v = E[i].v; if (E[i].w || E[i].eras) continue; if (--deg[v] == 0) topo.push(v); } } fu(i, 1, n) if (deg[i]) return 1; fu(i, 1, n) if (!vis[i]) topoid[++ind] = i; return 0; } void DP() { dp[1][0] = 1; fu(j, 0, k) { #ifdef Debug printf("In the case :: %d\n", j); #endif fu(id, 1, n) if (dp[topoid[id]][j]) { int u = topoid[id]; for (int i = head[u]; i; i = E[i].nxt) { if (E[i].eras || E[i].w) continue; int v = E[i].v; add(dp[v][j], dp[u][j]); #ifdef Debug printf("Trans between:[%d, %d] :: old value : %d new value : %d\n", u, v, j, E[i].w); #endif } } fu(id, 1, n) if (dp[topoid[id]][j]) { int u = topoid[id]; for (int i = head[u]; i; i = E[i].nxt) { if (E[i].eras || !E[i].w) continue; int v = E[i].v; if (j + E[i].w > k) continue; add(dp[v][j + E[i].w], dp[u][j]); #ifdef Debug printf("Trans between:[%d, %d] :: old value : %d new value : %d\n", u, v, j, E[i].w); #endif } } } } void solve() { Read(n), Read(m), Read(k), Read(p); init(); fu(i, 1, m) { int u, v, w; Read(u), Read(v), Read(w); addedge(u, v, w); } Dijkstra1(); Dijkstra2(); #ifdef Debug fu(i, 1, n) cout<<dis1[i]<<" "<<dis2[i]<<endl; #endif minidis = dis1[n]; if (dis1[n] >= INF_of_int) { printf("0\n"); return; } fu(i, 1, tot) { if (!E[i].dir) { if (dis1[E[i].u] + E[i].w + dis2[E[i].v] > minidis + k) E[i].eras = 1; } else E[i].eras = 1; } if (toposort()) { printf("-1\n"); return; } fu(i, 1, tot) { if (E[i].eras) continue; E[i].w = dis2[E[i].v] - dis2[E[i].u] + E[i].w; } toposort(); #ifdef Debug fu(i, 1, n) printf("%d ", topoid[i]); putchar('\n'); #endif DP(); int ans = 0; fu(i, 0, k) add(ans, dp[n][i]); Write(ans), putchar('\n'); } int main() { #ifdef dream_maker freopen("input.txt", "r", stdin); #endif int T; Read(T); while (T--) solve(); return 0; }