这个题听说是NOIP2017最难的题?反正我当时啥也不会爆零……
首先我们看送的分,直接跑最短路计数有30pts。
之后就不会了hhhhh……
我们仔细观察题目之后发现,这个题k的数据范围特别小,只有50,也就是说我们最多只会统计比最短路径长50的所有路径条数,那我们其实完全是可以这样DP的。
具体操作如下,我们首先建出正图,在正图上跑一遍dij,记录起点到每个点的最短距离。之后再建出反图,处理出有哪些点是能到达终点的,这样到不了终点的点就可以被忽略。
这样预处理之后,我们直接上大招:记忆化搜索!!!(这东西真的不是一般强力)
我们传两个参,一个是当前的节点,另一个是当前剩余的距离(因为我们最多允许走比最短路长k的距离,所以我们可以在记忆化搜索的时候把它作为参数传进去)。这样的话记忆化搜索就变得很简单。首先如果这个点无法到达终点,直接返回即可,如果这个点已经存过值(dp数组已经被更新过),直接返回这个值就可以(记忆化)。否则的话我们继续进行深搜,减去当前所多花费的路程继续向下DFS即可。
最后一个问题就是怎么判断0环。(因为如果出现无穷多条最短路的话一定是有0环出现)我们在记忆化搜索的时候使用vis数组记录当前是否被走过,在这个点结束dfs的时候将其还原。这样的话如果有0环,它会在vis被还原之前就走到这个点,那么就出现0环了。
这样的话我们就做完了……不过这题卡常很厉害……我也不会用线段树求最短路,最后还是开O2勉强卡过去了。
看一下代码。
// luogu-judger-enable-o2 #include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #include<vector> #include<set> #define pr pair<int,int> #define mp make_pair #define pb push_back #define fi first #define sc second #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; typedef long long ll; const int M = 100005; const int N = 10000005; const int INF = 2147483645; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,m,t,k,p,a,b,c,dis[M],dp[M][55],head[M],head1[M],ecnt,ecnt1; bool alive[M],vis[M][55]; struct edge { int next,to,v; }e[M<<1],e1[M<<1]; set <pr> q; set <pr> :: iterator it; void clear() { memset(e,0,sizeof(e)); memset(e1,0,sizeof(e1)); memset(head,0,sizeof(head)); memset(head1,0,sizeof(head1)); ecnt = ecnt1 = 0; rep(i,1,n) { alive[i] = 0,dis[i] = INF; rep(l,0,k) dp[i][l] = -1,vis[i][l] = 0; } } void add(int x,int y,int z) { e[++ecnt].to = y; e[ecnt].v = z; e[ecnt].next = head[x]; head[x] = ecnt; } void add1(int x,int y,int z) { e1[++ecnt1].to = y; e1[ecnt1].v = z; e1[ecnt1].next = head1[x]; head1[x] = ecnt1; } void dij() { int cnt = 0; dis[1] = 0; q.insert(mp(dis[1],1)); while(!q.empty()) { pr k = *(q.begin()); q.erase(q.begin()); for(int i = head[k.sc];i;i = e[i].next) { if(dis[e[i].to] > dis[k.sc] + e[i].v) { it = q.find(mp(dis[e[i].to],e[i].to)); if(it != q.end()) q.erase(it); dis[e[i].to] = dis[k.sc] + e[i].v; q.insert(mp(dis[e[i].to],e[i].to)); } } } } void bfs() { queue <int> qu; qu.push(n);alive[n] = 1; while(!qu.empty()) { int k = qu.front();qu.pop(); for(int i = head1[k];i;i = e1[i].next) { int g = e1[i].to; if(!alive[g]) alive[g] = 1,qu.push(g); } } } int dfs(int a,int b) { if(b < 0) return 0; if(vis[a][b]) return -INF; if(dp[a][b] != -1) return dp[a][b]; vis[a][b] = 1; int cur = 0; if(a == n) cur++; for(int i = head[a];i;i = e[i].next) { int di = dis[e[i].to] - dis[a]; if(!alive[e[i].to]) continue; int w = dfs(e[i].to,b - (e[i].v - di)); if(w == -INF) return -INF; else { cur += w; if(cur > p) cur -= p; } } dp[a][b] = cur % p; vis[a][b] = 0; return cur; } int main() { t = read(); while(t--) { n = read(),m = read(),k = read(),p = read(); clear(); rep(i,1,m) { a = read(),b = read(),c = read(); add(a,b,c),add1(b,a,c); } dij();//rep(i,1,n) printf("%d ",dis[i]);enter; bfs();//rep(i,1,n) printf("%d ",alive[i]);enter; int ans = dfs(1,k); if(ans == -INF) printf("-1 "); else printf("%d ",ans); } return 0; }