时隔将近一年,我终于开始捡起去年NOIP的题了。记得当时啥也不会的我看到这题无从下手,就连暴力也没写出来。
好吧,说正事。
1.如果k = 0的话很好做,就是最短路计数。
2.不过这道题允许走“冤枉路”,于是我们可以dp。考虑到每走一条边,就会对冤枉路的长度做出贡献,那么令dp[i][j]表示到节点 i ,还可以走 j 长度的冤枉路。转移就是很直观的方案数累加的转移。
3.虽然题目保证了1可以走到n,但是不能保证所有点都能走到n,所以有一些状态是走不到的。因此,我们还要反向见图跑最短路,求出哪些点可以到达n。
4.因为可能有环,dp顺序不好确定,那么就来一波记搜吧。(然而我几乎没写过记搜,只能现学)
5.考虑方案数无穷的情况:其实就是存在零环,只要开一个二维标记数组,在dfs时判断从该状态出发,是否又回到这个状态就行。
6.这题卡常啊,我使出浑身解数用上了所有卡常手段才过……
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 1e5 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) {last = ch; ch = getchar();} 26 while(isdigit(ch)) {ans = (ans << 3) + (ans << 1) + ch - '0'; ch = getchar();} 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 37 int n, m, k, mod; 38 vector<int> v[maxn], c[maxn]; 39 40 #define pr pair<int, int> 41 #define mp make_pair 42 bool in[maxn]; 43 int dis1[maxn]; 44 inline void dij1(const int& s) 45 { 46 for(int i = 1; i <= n; ++i) dis1[i] = INF, in[i] = 0; 47 dis1[s] = 0; 48 priority_queue<pr, vector<pr>, greater<pr> > q; 49 q.push(mp(dis1[s], s)); 50 while(!q.empty()) 51 { 52 int now = q.top().second; q.pop(); 53 if(in[now]) continue; 54 in[now] = 1; 55 for(int i = 0; i < (int)v[now].size(); ++i) 56 { 57 if(dis1[v[now][i]] > dis1[now] + c[now][i]) 58 { 59 dis1[v[now][i]] = dis1[now] + c[now][i]; 60 q.push(mp(dis1[v[now][i]], v[now][i])); 61 } 62 } 63 } 64 } 65 vector<int> v2[maxn], c2[maxn]; 66 int dis2[maxn]; 67 inline void dij2(const int& s) 68 { 69 for(rg int i = 1; i <= n; ++i) dis2[i] = INF, in[i] = 0; 70 dis2[s] = 0; 71 priority_queue<pr, vector<pr>, greater<pr> > q; 72 q.push(mp(dis2[s], s)); 73 while(!q.empty()) 74 { 75 int now = q.top().second; q.pop(); 76 if(in[now]) continue; 77 in[now] = 1; 78 for(rg int i = 0; i < (int)v2[now].size(); ++i) 79 { 80 if(dis2[v2[now][i]] > dis2[now] + c2[now][i]) 81 { 82 dis2[v2[now][i]] = dis2[now] + c2[now][i]; 83 q.push(mp(dis2[v2[now][i]], v2[now][i])); 84 } 85 } 86 } 87 } 88 89 bool vis[maxn][55]; 90 int dp[maxn][55]; 91 inline int dfs(const int& now, const int& res) 92 { 93 if(res < 0) return 0; 94 if(vis[now][res]) return -1; 95 if(dp[now][res] != -1) return dp[now][res]; 96 vis[now][res] = 1; 97 int ret = 0; 98 if(now == n) ret++; 99 for(rg int i = 0; i < (int)v[now].size(); ++i) 100 { 101 int to = v[now][i]; 102 if(dis2[to] == INF) continue; 103 int tp = dfs(to, res - c[now][i] + dis1[to] - dis1[now]); 104 if(tp == -1) return -1; 105 ret += tp; if(ret > mod) ret -= mod; 106 } 107 dp[now][res] = ret; 108 vis[now][res] = 0; //别忘清零 109 return ret; 110 } 111 112 inline void init(const int& n) 113 { 114 for(rg int i = 1; i <= n; ++i) 115 { 116 v[i].clear(), c[i].clear(); 117 v2[i].clear(); c2[i].clear(); 118 } 119 for(rg int i = 1; i <= n; ++i) 120 for(rg int j = 0; j <= k; ++j) dp[i][j] = -1, vis[i][j] = 0; 121 } 122 123 int main() 124 { 125 int T = read(); 126 while(T--) 127 { 128 n = read(); m = read(); k = read(); mod = read(); 129 init(n); 130 for(rg int i = 1; i <= m; ++i) 131 { 132 int x = read(), y = read(), co = read(); 133 v[x].push_back(y); c[x].push_back(co); 134 v2[y].push_back(x); c2[y].push_back(co); 135 } 136 dij1(1); dij2(n); 137 write(dfs(1, k)); enter; 138 } 139 return 0; 140 }