1. 程式人生 > >[Noip2017]逛公園

[Noip2017]逛公園

sed close 路線 spfa 技術 難受 其他 class bool

其實真的是一道思博題啊。為啥考試我就沒想出來??

因為長得太拓撲了(其他正解好像也確實是拓撲),然後顧忌到環就沒想。太菜了還是。

後來上課閑的沒事一想,靠,記憶化搜索啊。這樣一定是所有父親都計算完不重不漏。

(正著想不也是嘛)

很難受,要不然能多點分。


【問題描述】

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

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

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

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

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

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

【輸入格式】

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

接下來$T$組數據,對於每組數據:

第一行包含四個整數 $N,M,K,P$, 每兩個整數之間用一個空格隔開。

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

【輸出格式】

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

對於第一組數據,最短路為 3。

1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 為 3 條合法路徑。


用$dp[u][k]$表示從$1$到$u$點的所有路徑裏,比最短路多$k$的路徑數。

則有

$dp[u][k]=\sum dp[v][dis[u]+k-w-dis[v]]$

$dis[x]$是從$1$到$x$的最短路。$w$是邊權。

記憶化搜索即可。判0環的話,還沒有寫出完美的寫法,如果有不包含$1$的0環,那麽一個未確定的$dp$值被訪問兩次說明存在。

復雜度$O(nk)$。

代碼

技術分享圖片
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include
<string> #include<queue> #include<deque> #include<stack> #include<vector> using namespace std; const int N=100010; const int inf=0x3f3f3f3f; typedef long long ll; inline int read(){ int r=0,f=1,c=getchar(); while(c<0||c>9){if(c==-)f=-1;c=getchar();} while(c>=0&&c<=9){r=r*10+c-0;c=getchar();} return r*f; } struct Edge{ int to,nxt,w; }e[N*2],E[N*2]; int head[N],Head[N],cnt=1; void add(int u,int v,int w){ e[cnt]=(Edge){v,head[u],w}; head[u]=cnt; E[cnt]=(Edge){u,Head[v],w}; Head[v]=cnt++; } int n,m,K,P; bool inq[N]; int dis[N],f[N][51],c[N][51]; void spfa(){ deque<int>q; memset(dis,63,sizeof dis); dis[1]=0;q.push_back(1);inq[1]=1; while(!q.empty()){ int u=q.front();q.pop_front();inq[u]=0; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to,w=e[i].w; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; if(!inq[v]){ if(!q.empty()&&dis[v]<=dis[q.front()]) q.push_front(v); else q.push_back(v); } } } } }bool ff=0; void Clear(){ n=m=K=P=0; memset(inq,0,sizeof inq); memset(f,-1,sizeof f); memset(head,0,sizeof head); memset(Head,0,sizeof Head); memset(c,0,sizeof c); memset(e,0,sizeof e); memset(E,0,sizeof E);cnt=1; ff=0; } void Init(){ n=read(),m=read(),K=read(),P=read(); while(m--){ int u=read(),v=read(),w=read(); add(u,v,w); } } int dfs(int u,int k){ if(~f[u][k])return f[u][k]; c[u][k]=1; f[u][k]=0; for(int i=Head[u];i;i=E[i].nxt){ int v=E[i].to,w=E[i].w; int t=dis[u]+k-w-dis[v]; if(t<0)continue; if(c[v][t])ff=1; f[u][k]+=dfs(v,t),f[u][k]%=P; } c[u][k]=0; return f[u][k]; } void Solve(){ spfa(); f[1][0]=1; int ans=0; for(int j=0;j<=K;j++) ans+=dfs(n,j),ans%=P; dfs(n,K+1); if(ff){ cout<<-1<<endl; return ; } printf("%d\n",ans); } int main(){ int T=read(); while(T--){ Clear(); Init(); Solve(); } return 0; }
View Code

[Noip2017]逛公園