noip2017 D1T3 逛公园 某zz选手看到数据范围直接就最短路计数了,结果写错了爆零
题目大意:
N个点M条边构成的有向图,且没有自环和重边。其中1号点是起点,N号点是公园的终点,每条边有一个非负权值, 代表经过这条边所要花的时间
如果1号点到N号点的最短路长为d,那么策策只选择长度不超过d + K的路线
求总共有多少条满足条件的路线
为避免输出过大,答案对P取模。
如果有无穷多条合法的路线,请输出−1
思路:
首先需要求出最短路用spfa
然后我们dfs的时候dp
具体见注释
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #define inf 2139062143 10 #define ll long long 11 #define MAXN 100100 12 using namespace std; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 17 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 int T,n,m,k,MOD; 21 int cnt,nxt[MAXN*2],fst[MAXN],to[MAXN*2],val[MAXN*2]; 22 int Cnt,Nxt[MAXN*2],Fst[MAXN],To[MAXN*2],Val[MAXN*2]; 23 int dis[MAXN],dp[MAXN][55]; 24 bool vis[MAXN],jdg[MAXN][55],f; 25 void add(int u,int v,int w) {nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v,val[cnt]=w;} 26 void Add(int u,int v,int w) {Nxt[++Cnt]=Fst[u],Fst[u]=Cnt,To[Cnt]=v,Val[Cnt]=w;} 27 void spfa() 28 { 29 memset(dis,127,sizeof(dis)); 30 queue <int> q; 31 dis[1]=0,vis[1]=1;q.push(1); 32 while(!q.empty()) 33 { 34 int k=q.front(); 35 q.pop();vis[k]=0; 36 for(int i=fst[k];i;i=nxt[i]) 37 if(dis[k]+val[i]<dis[to[i]]) {dis[to[i]]=dis[k]+val[i];if(!vis[to[i]]){vis[to[i]]=1;q.push(to[i]);}} 38 } 39 } 40 int dfs(int x,int ext)//表示走到x节点刚好多走了ext dfs的时候按照反向边走 41 { 42 if(dp[x][ext]!=-1) return dp[x][ext]; 43 jdg[x][ext]=1,dp[x][ext]=0;//jdg用来判零环 (如果一个点相同的ext在dp还未被确定的情况下被访问了两遍,说明有零环) 44 for(int i=Fst[x];i;i=Nxt[i]) 45 { 46 if(dis[x]-dis[To[i]]+ext-Val[i]<0) continue;//这么长的一大串表示按这条边走的ext <0说明不能按这条边走 47 if(jdg[To[i]][dis[x]-dis[To[i]]+ext-Val[i]]) f=1;//有零环 48 (dp[x][ext]+=dfs(To[i],dis[x]-dis[To[i]]+ext-Val[i]))%=MOD;//接着dfs 49 } 50 jdg[x][ext]=0; 51 return dp[x][ext]; 52 } 53 int main() 54 { 55 T=read(); 56 int a,b,c,ans=0; 57 while(T--) 58 { 59 memset(nxt,0,sizeof(nxt)); 60 memset(Nxt,0,sizeof(Nxt)); 61 memset(Fst,0,sizeof(Fst)); 62 memset(fst,0,sizeof(fst)); 63 memset(jdg,0,sizeof(jdg)); 64 memset(vis,0,sizeof(vis)); 65 memset(dp,0xff,sizeof(dp));//设为-1是因为在这个dp里 0也是一种合法的结果 66 n=read(),m=read(),k=read(),MOD=read(),cnt=Cnt=0; 67 for(int i=1;i<=m;i++) {a=read(),b=read(),c=read();add(a,b,c);Add(b,a,c);} 68 spfa();//处理出最短路 69 ans=f=0,dp[1][0]=1; 70 for(int i=0;i<=k;i++) (ans+=dfs(n,i))%=MOD;//倒着dfs 在搜索的过程中能够非常巧妙地判断零环 71 dfs(n,k+1);//用来判断k==0时有零环的情况 72 if(f) puts("-1"); 73 else printf("%d ",ans); 74 } 75 }