就快把NOIP的题都做完了(事实上剩下的题都是最毒瘤的,比如天天爱跑步
正解暂时还不会写,在这里先贴一个分层图的题解(会被卡30分,Luogu上开O2才能过
分层图:
由于
k
<
=
50
k<=50
k<=50,把每个点拆成
k
+
1
k+1
k+1个点,表示经过该点时,超出最短路长度
j
j
j的情况。
首先,预处理1到所有点的最短路。
然后连边,如果当前点到另一个点多出来的总距离不超过1到那个点的最短距离+k 就连一条边。
然后拓扑排序,每走一步累加方案数。
有零环在满足条件的路径上的时候:注意到一个点如果是零环的一部分,则该点度数永远不可能为0。
原因很简单,因为一个点只会被小于等于该层数的点更新,所以如果有一个零环位于满足条件的路径上,则这个环上的每一个节点都不会被选到,因为它们的入度始终大于0。而一个非零环呢?因为这个环被分层图拆成了一条链,所以拓扑排序没有问题。
最后判断是否有到n点后度数大于0的点,如果有就存在零环。(如果判断每一个点,则可能会有不在满足路径上的零环被选中。)
#pragma GCC optimize(2)
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 100001;
const int MAXM = 200001;
int fir[MAXN], nxt[MAXM], to[MAXM], len[MAXM], cnt;
int dis[MAXN], vis[MAXN], dp[MAXN][51], du[MAXN][51];
struct Node{
int u, dis;
bool operator < (const Node &a) const{
return dis > a.dis;
}
};
priority_queue <Node> q;
inline char nc(){
static char buf[100000], *p1 = buf, *p2 = buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
int k = 0; char ch = nc();
while(ch < '0' || ch > '9') ch = nc();
while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0', ch = nc();}
return k;
}
inline void add_edge(int a, int b, int l){
len[cnt] = l;
to[cnt] = b;
nxt[cnt] = fir[a];
fir[a] = cnt++;
}
void Dijkstra(int u){
memset(vis, false, sizeof(vis));
memset(dis, 0x3f, sizeof(dis)); dis[u] = 0;
q.push((Node){u, 0});
while(!q.empty()){
u = q.top().u; q.pop();
if(vis[u]) continue;
vis[u] = true;
for(int i = fir[u]; i != -1; i = nxt[i]){
int v = to[i];
if(dis[v] > dis[u] + len[i]){
dis[v] = dis[u] + len[i];
q.push((Node){v, dis[v]});
}
}
}
}
int main(){
int T = read();
while(T--){
memset(fir, -1, sizeof(fir)); cnt = 0;
memset(du, 0, sizeof(du));
memset(dp, 0, sizeof(dp));
int n = read(), m = read(), k = read(), p = read();
for(int i = 1; i <= m; i++){
int a = read(), b = read(), l = read();
add_edge(a, b, l);
}
Dijkstra(1);
for(int u = 1; u <= n; u++){
for(int i = fir[u]; i != -1; i = nxt[i]){
int v = to[i];
for(int j = 0; j <= k; j++){
if(dis[u] + len[i] + j - dis[v] <= k){
du[v][dis[u] + len[i] + j - dis[v]]++;
}
else break;
}
}
}
queue < pair<int, int> > q;
for(int u = 1; u <= n; u++){
for(int j = 0; j <= k; j++){
if(!du[u][j]) q.push(make_pair(u, j));
}
}
dp[1][0] = 1;
while(!q.empty()){
int u = q.front().first, k1 = q.front().second; q.pop();
for(int i = fir[u]; i != -1; i = nxt[i]){
int v = to[i], k2 = dis[u] + len[i] + k1 - dis[v];
if(k2 > k) continue;
dp[v][k2] += dp[u][k1];
if(dp[v][k2] >= p) dp[v][k2] -= p;
if(--du[v][k2] == 0){
q.push(make_pair(v, k2));
}
}
}
int Ans = 0, flag = false;
for(int i = 0; i <= k; i++){
if(du[n][i] != 0) flag = true;
Ans = (Ans + dp[n][i]) % p;
}
if(flag) puts("-1");
else printf("%d\n", Ans);
}
return 0;
}