日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[NOIP2017]逛公园 最短路+拓扑排序+dp

發布時間:2025/6/17 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [NOIP2017]逛公园 最短路+拓扑排序+dp 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目描述

給出一張 $n$ 個點 $m$ 條邊的有向圖,邊權為非負整數。求滿足路徑長度小于等于 $1$ 到 $n$ 最短路 $+k$ 的 $1$ 到 $n$ 的路徑條數模 $p$ ,如果有無數條則輸出 $-1$ 。

輸入

第一行包含一個整數 $T$ , 代表數據組數。

接下來 $T$ 組數據,對于每組數據: 第一行包含四個整數 $N,M,K,P$ ,每兩個整數之間用一個空格隔開。

接下來 $M$ 行,每行三個整數 $a_i,b_i,c_i$ ,代表編號為 $a_i,b_i$ 的點之間有一條權值為 $c_i$ 的有向邊,每兩個整數之間用一個空格隔開。

輸出

輸出文件包含 $T$ 行,每行一個整數表示答案。

樣例輸入

2
5 7 2 10
1 2 1
2 4 0
4 5 2
2 3 2
3 4 1
3 5 2
1 5 3
2 2 0 10
1 2 0
2 1 0

樣例輸出

3
-1


題解

最短路+拓撲排序+dp

首先使用堆優化Dijkstra或Spfa(不知道本題是否會卡)求出1到所有點的最短路。

由于對于所有邊 $(x,y,z)$ 滿足 $dis[x]+z\ge dis[y]$ ,因此超過最短路的部分不會減少。

那么我們設 $f[i][j]$ 表示到達點 $i$ 時經過的路徑總長度為 $dis[i]+j\ (j \le k)$ 的方案數。那么這相當于一個新的分層圖,只會在同層或向上層轉移,不會像下層轉移。

這就轉化為圖上求路徑條數。首先初始化 $f[1][0]=0 $ ,跑拓撲排序的同時進行轉移。

如果一個點被排到了,那么 $f$ 值即為路徑條數。

如果一個點沒有被排到,則說明有環連接到它,即路徑條數為 $\infty$。

因此把所有 $f[n][0...k]$ 統計一下即可。

時間復雜度 $O(T(m\log n+mk))$

考場上一眼看出題解,然而卡了兩個小時的常數才勉強卡進去...

考場原代碼(去掉了文件操作):

#include <queue> #include <cctype> #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 #define M 200010 #define R register #define pos(x , y) (x + (y) * n) using namespace std; typedef pair<int , int> pr; priority_queue<pr> q; int head[N] , to[M] , len[M] , next[M] , cnt , dis[N] , vis[N]; int ll[N * 51] , rr[N * 51] , tt[M * 51] , que[N * 51] , ind[N * 51] , f[N * 51]; inline void add(int x , int y , int z) {to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt; } inline char nc() {static char buf[100000] , *p1 , *p2;return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ; } inline int read() {int ret = 0; char ch = nc();while(!isdigit(ch)) ch = nc();while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc();return ret; } int main() {int T = read();while(T -- ){memset(head , 0 , sizeof(head));memset(vis , 0 , sizeof(vis));memset(ind , 0 , sizeof(ind));memset(f , 0 , sizeof(f));cnt = 0;int n = read() , m = read() , k = read() , z , ans = 0 , flag = 1;R int p = read() , i , j , x , y , l = 1 , r = 0;for(i = 1 ; i <= m ; ++i) x = read() , y = read() , z = read() , add(x , y , z);memset(dis , 0x3f , sizeof(dis));dis[1] = 0 , q.push(pr(0 , 1));while(!q.empty()){x = q.top().second , q.pop();if(vis[x]) continue;vis[x] = 1;for(i = head[x] ; i ; i = next[i])if(dis[to[i]] > dis[x] + len[i])dis[to[i]] = dis[x] + len[i] , q.push(pr(-dis[to[i]] , to[i]));}cnt = 0;for(x = 1 ; x <= n ; ++x){for(j = 0 ; j <= k ; ++j){ll[pos(x , j)] = cnt + 1;for(i = head[x] ; i ; i = next[i])if(j + dis[x] + len[i] - dis[to[i]] <= k)++ind[tt[++cnt] = pos(to[i] , j + dis[x] + len[i] - dis[to[i]])];rr[pos(x , j)] = cnt;}}f[1] = 1;for(x = 1 ; x <= pos(n , k) ; ++x)if(!ind[x])que[++r] = x;while(l <= r){x = que[l ++ ];for(i = ll[x] ; i <= rr[x] ; ++i){y = tt[i];f[y] += f[x] , ind[y] -- ;if(f[y] >= p) f[y] -= p;if(!ind[y]) que[++r] = y;}}for(i = 0 ; i <= k ; ++i){if(ind[pos(n , i)]) flag = 0;ans = (ans + f[pos(n , i)]) % p;}if(flag) printf("%d\n" , ans);else puts("-1");}return 0; }

?

轉載于:https://www.cnblogs.com/GXZlegend/p/7838900.html

總結

以上是生活随笔為你收集整理的[NOIP2017]逛公园 最短路+拓扑排序+dp的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。