1. 程式人生 > >NOIP2017 逛公園 分層圖+拓撲排序

NOIP2017 逛公園 分層圖+拓撲排序

就快把NOIP的題都做完了(事實上剩下的題都是最毒瘤的,比如天天愛跑步 正解暫時還不會寫,在這裡先貼一個分層圖的題解(會被卡30分,Luogu上開O2才能過

分層圖: 由於k<=50,把每個點拆成k+1個點,表示經過該點時,超出最短路長度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; }