[Noip2017]逛公园
其实真的是一道思博题啊。为啥考试我就没想出来??
因为长得太拓扑了(其他正解好像也确实是拓扑),然后顾忌到环就没想。太菜了还是。
后来上课闲的没事一想,靠,记忆化搜索啊。这样一定是所有父亲都计算完不重不漏。
(正着想不也是嘛)
很难受,要不然能多点分。
【问题描述】
策策同学特别喜欢逛公园。 公园可以看成一张$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$的最短路。spfa预处理复杂度$O(n \times ??? )$。
$w$是边权。$v$是所有有指向$u$的边的点。
记忆化搜索即可。判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; }