1. 程式人生 > >【Noip2017 逛公園】

【Noip2017 逛公園】

ring %d size 路徑 否則 tom 總數 ace empty

題目描述

  策策同學特別喜歡逛公園。公園可以看成一張N個點M條邊構成的有向圖,且沒有 自環和重邊。其中1號點是公園的入口,N號點是公園的出口,每條邊有一個非負權值, 代表策策經過這條邊所要花的時間。

  策策每天都會去逛公園,他總是從1號點進去,從N號點出來。

  策策喜歡新鮮的事物,它不希望有兩天逛公園的路線完全一樣,同時策策還是一個 特別熱愛學習的好孩子,它不希望每天在逛公園這件事上花費太多的時間。如果1號點 到N號點的最短路長為d,那麽策策只會喜歡長度不超過d+K的路線。

  策策同學想知道總共有多少條滿足條件的路線,你能幫幫它嗎?

  為避免輸出過大,答案對P取模。

  如果有無窮多條合法的路線,請輸出?1。

輸入格式:

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

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

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

輸出格式:

  輸出文件包含 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,f[k][u]表示到達u這個點時距離為dis[u]+k的路徑總數(否則空間存不下,並且發現K<=50),然後轉移,f[k+dis[u]+w-dis[v]][v]+=f[k][u]。

  然後會發現轉移順序在邊權為0或者在最短路上會出錯,因為這時轉移是f[k][v]+=f[k][u],但是必須要先轉移f[k][u]。

  因此要把圖拓撲下求出在同樣k時候的更新順序即可。

  判斷無解的話就是有一個點在0環中且經過這個點的路勁長度滿足<=dis[n]+k。

  1 #include<iostream>
  2 #include<cstdio>
  3
#include<cstring> 4 #include<cstdlib> 5 #include<queue> 6 using namespace std; 7 struct node{ 8 int to,next,v; 9 } e[200010],g[200010]; 10 int cnt,tot; 11 int head[100010],last[100010]; 12 int vis[100010],n,m,K,P,dis1[100010],dis2[100010],in[100010]; 13 int f[55][100010]; 14 priority_queue<pair<int,int> > q; 15 int s[100010]; 16 inline void insert(int u,int v,int w){ 17 e[++cnt].next=head[u]; 18 head[u]=cnt; 19 e[cnt].to=v; 20 e[cnt].v=w; 21 } 22 inline void add(int u,int v,int w){ 23 g[++tot].next=last[u]; 24 last[u]=tot; 25 g[tot].to=v; 26 g[tot].v=w; 27 } 28 inline void dj1(){ 29 memset(vis,0,sizeof(vis)); 30 for(int i=1;i<=n;i++) dis1[i]=1e9+7; 31 dis1[1]=0; 32 q.push(make_pair(0,1)); 33 while(!q.empty()){ 34 int now=q.top().second;q.pop(); 35 if(vis[now]) continue; 36 vis[now]=1; 37 for(int i=head[now];i;i=e[i].next){ 38 if(dis1[now]+e[i].v<dis1[e[i].to]){ 39 dis1[e[i].to]=dis1[now]+e[i].v; 40 q.push(make_pair(-dis1[e[i].to],e[i].to)); 41 } 42 } 43 } 44 } 45 inline void dj2(){ 46 memset(vis,0,sizeof(vis)); 47 for(int i=1;i<=n;i++) dis2[i]=1e9+7; 48 dis2[n]=0; 49 q.push(make_pair(0,n)); 50 while(!q.empty()){ 51 int now=q.top().second;q.pop(); 52 if(vis[now]) continue; 53 vis[now]=1; 54 for(int i=last[now];i;i=g[i].next){ 55 if(dis2[now]+g[i].v<dis2[g[i].to]){ 56 dis2[g[i].to]=dis2[now]+g[i].v; 57 q.push(make_pair(-dis2[g[i].to],g[i].to)); 58 } 59 } 60 } 61 } 62 inline void dp(){ 63 int ans=0; 64 memset(f,0,sizeof(f)); 65 f[0][1]=1; 66 for(int k=0;k<=K;k++){ 67 for(int i=1;i<=s[0];i++){ 68 for(int now=s[i],j=head[now];j;j=e[j].next) 69 if(k+dis1[now]+e[j].v-dis1[e[j].to]<=K) 70 f[k+dis1[now]+e[j].v-dis1[e[j].to]][e[j].to]=(f[k][now]+f[k+dis1[now]+e[j].v-dis1[e[j].to]][e[j].to])%P; 71 } 72 } 73 for(int i=0;i<=K;i++) 74 ans=(ans+f[i][n])%P; 75 printf("%d\n",ans); 76 } 77 inline void solve(){ 78 queue<int> p; 79 scanf("%d%d%d%d",&n,&m,&K,&P); 80 memset(head,0,sizeof(head)); 81 memset(last,0,sizeof(last)); 82 memset(s,0,sizeof(s)); 83 memset(in,0,sizeof(in)); 84 cnt=tot=0; 85 int u,v,w; 86 for(int i=1;i<=m;i++){ 87 scanf("%d%d%d",&u,&v,&w); 88 insert(u,v,w); 89 add(v,u,w); 90 } 91 dj1();dj2(); 92 for(int i=1;i<=n;i++){ 93 for(int j=head[i];j;j=e[j].next){ 94 if(dis1[i]+e[j].v==dis1[e[j].to]) in[e[j].to]++; 95 } 96 } 97 for(int i=1;i<=n;i++){ 98 if(!in[i]){ 99 s[++s[0]]=i; 100 p.push(i); 101 } 102 } 103 while(!p.empty()){ 104 int now=p.front();p.pop(); 105 for(int j=head[now];j;j=e[j].next){ 106 if(dis1[now]+e[j].v==dis1[e[j].to]){ 107 in[e[j].to]--; 108 if(!in[e[j].to]){ 109 p.push(e[j].to); 110 s[++s[0]]=e[j].to; 111 } 112 } 113 } 114 } 115 for(int i=1;i<=n;i++){ 116 if(in[i] && dis1[i]+dis2[i]<=dis1[n]+K){ 117 printf("-1\n"); 118 return; 119 } 120 } 121 dp(); 122 } 123 int main(){ 124 int T; 125 scanf("%d",&T); 126 while(T--){ 127 solve(); 128 } 129 return 0; 130 }

【Noip2017 逛公園】