LOJ2316. 「NOIP2017」逛公园【DP】【最短路】【思维】
思路
因为我想到的根本不是网上的普遍做法
所以常数出奇的大,而且做法极其暴力
可以形容是带优化的大模拟
进入正题:
首先一个很显然的思路是如果在合法的路径网络里面存在零环是有无数组解的
然后这个直接对所有边权是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;
}