NOIP2017 逛公園 分層圖+拓撲排序
阿新 • • 發佈:2018-12-09
就快把NOIP的題都做完了(事實上剩下的題都是最毒瘤的,比如天天愛跑步 正解暫時還不會寫,在這裡先貼一個分層圖的題解(會被卡30分,Luogu上開O2才能過
分層圖: 由於,把每個點拆成個點,表示經過該點時,超出最短路長度的情況。 首先,預處理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;
}