[NOIP2017]逛公园
题目:洛谷P3953、Vijos P2030、UOJ#331。
题目大意:
给你\(N\)点\(M\)边的有向无权图,没有负权但有零权。求\(1\)到\(N\)的路径中长度不超过\(d+K\)的路径的条数\(\mod P\),无穷输出\(-1\)。
解题思路:
考场上我写的貌似接近正解的dp,只是先判了零环,然后传递值的时候比较暴力,疯狂优化常数后拿了70分。
正解也是dp。
设\(f_{i,j}\)表示到\(i\)号点,比最短路径多\(j\)的路径条数,\(d_i\)表示到\(i\)的最短路径长度。
则\(f_{i,j}=\sum f_{p,d_p-d_i-e+j} \)(\(e\)为\(p\)到\(i\)的一条边权长,且\({p,d_p-d_i-e+j}\geqslant 0\))
边界\(f_{0,0}=1\)(需要加一条\(0\)到\(1\)的零边并作为起点,否则样例的零环会挂)。
我们可以用记忆化搜索计算\(f\),当状态\({i,j}\)出现了两次,则一定出现了零环,退出输出\(-1\)即可。
时间复杂度貌似有点玄学,但还是过了(最坏\(O(NK^2)\)?)。
C++ Code:
#include<bits/stdc++.h> #define M 200005 #define N 100005 inline int readint(){ int c=getchar(),d=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) d=(d<<3)+(d<<1)+(c^'0'); return d; } struct edge{ int to,dis,nxt; }e[M],e2[M]; int head[N],head2[N],n,m,k,p,cnt1,cnt2,dis[N],l,r,q[130005]; int dp[N][52]; bool vis[N],ur[N][52],ok; inline int add1(const int u,const int v,const int t){ e[++cnt1]=(edge){v,t,head[u]}; head[u]=cnt1; } inline int add2(const int u,const int v,const int t){ e2[++cnt2]=(edge){u,t,head2[v]}; head2[v]=cnt2; } int dfs(int u,int k){ if(~dp[u][k])return dp[u][k]; dp[u][k]=0,ur[u][k]=1; for(int i=head2[u];i!=-1&&ok;i=e2[i].nxt){ int v=e2[i].to; int d=dis[u]-dis[v]-e2[i].dis+k; if(d<0)continue; if(ur[v][d]){ ok=0;return 0; } dp[u][k]=(dp[u][k]+dfs(v,d))%p; } ur[u][k]=0; return dp[u][k]; } int main(){ for(int T=readint();T--;){ memset(head,-1,sizeof head); memset(head2,-1,sizeof head2); memset(e,0,sizeof e); memset(e2,0,sizeof e2); cnt1=cnt2=0; n=readint(),m=readint(),k=readint(),p=readint(); while(m--){ int u=readint(),v=readint(),d=readint(); add1(u,v,d); add2(u,v,d); } add1(0,1,0),add2(0,1,0); memset(dis,0x3f,sizeof dis); memset(vis,0,sizeof vis); dis[0]=0; l=q[1]=0,r=vis[0]=1;int u; while(l!=r){ vis[u=q[l=l%130000+1]]=0; for(int i=head[u];i!=-1;i=e[i].nxt) if(dis[e[i].to]>dis[u]+e[i].dis){ dis[e[i].to]=dis[u]+e[i].dis; if(!vis[e[i].to])vis[q[r=r%130000+1]=e[i].to]=1; } } memset(dp,-1,sizeof dp); memset(ur,0,sizeof ur); ok=dp[0][0]=1; int ans=0; for(int i=0;i<=k&&ok;++i)ans=(ans+dfs(n,i))%p; printf("%d\n",ok?ans:-1); } return 0; }