1. 程式人生 > >LOJ2316. 「NOIP2017」逛公園【DP】【最短路】【思維】

LOJ2316. 「NOIP2017」逛公園【DP】【最短路】【思維】

LINK

思路

因為我想到的根本不是網上的普遍做法

所以常數出奇的大,而且做法極其暴力

可以形容是帶優化的大模擬

進入正題:

首先一個很顯然的思路是如果在合法的路徑網路裡面存在零環是有無陣列解的

然後這個直接對所有邊權是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;
}