题意想必大家都很清楚,(其实是特水的作者太懒了)
算法:(其一)
首先对于原图的每一条边正着建在一张图中,再反着建在一张图中,我们对于那个反向图跑一遍SPFA,处理出点N到每一个点的最短路径的长度。
然后用记忆化搜索,dfs(u,now)表示反向图中从u跑到n用了(最短路径+now)的路径数,至于状态的转移各位直接看程序即可。
特别的,我们每次用一个hash记录一个状态我们是否计算过,如果计算过了,则说明这里面有个万恶万恶万恶的0环,咱们直接输出-1即可。
AC代码:
/*
Name: park
Copyright: author's
Author: wangziyao
Date: 11/11/17 8:30
Description: NOIP2017 T_D1_T3
*/
#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define r(a); a=wzy.read();
#define w(a); wzy.write(a);
using namespace std;
class WZY{
public:
inline int read(){
char c=getchar();int num=0;int f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){num=num*10+(c-'0');c=getchar();}
return num*f;
}
inline void qwq(int x){
if(x>9)qwq(x/10);
putchar(x%10+'0');
}
inline void write(int x){
if(x<0){x=-x;putchar('-');}
qwq(x);putchar('\n');
}
}wzy;
int n,m,k,p;
struct edge1{
int next,vertice,value;
}zh[200010];
struct edge2{
int next,vertice,value;
}fa[200010];
int len=0;
int zhead[100010];int fhead[100010];
void add_edge(int x,int y,int c){
zh[++len].next=zhead[x];zh[len].vertice=y;zh[len].value=c;zhead[x]=len;
fa[len].next=fhead[y];fa[len].vertice=x;fa[len].value=c;fhead[y]=len;
return;
}
queue<int>v;
int dis[100010];bool in[100010];
void spfa(){
memset(dis,0x3f,sizeof(dis));
memset(in,0,sizeof(in));
v.push(n);dis[n]=0;in[n]=1;
while(!v.empty()){
int nop=v.front();v.pop();in[nop]=0;
for(int i=fhead[nop];i;i=fa[i].next){
int nop1=fa[i].vertice;int nop2=fa[i].value;
if(dis[nop1]>dis[nop]+nop2){
dis[nop1]=dis[nop]+nop2;
if(!in[nop1]){v.push(nop1);in[nop1]=1;}
}
}
}
return;
}
int dp[100010][60];bool hashs[100010][60];
bool flag=true;
int dfs(int now,int tmp){
if(hashs[now][tmp]){flag=false;return 0;}
if(dp[now][tmp]!=-1)return dp[now][tmp];
hashs[now][tmp]=1;
int re_value=0;
for(int i=zhead[now];i;i=zh[i].next){
int nop1=zh[i].vertice;int nop2=zh[i].value;
int temp=tmp-(dis[nop1]+nop2-dis[now]);if(temp<0||temp>k)continue;
re_value=(re_value+dfs(nop1,temp))%p;
if(flag==false)return 0;
}
if(now==n&&tmp==0)re_value=1;
hashs[now][tmp]=0;
dp[now][tmp]=re_value;
return re_value;
}
int main(){
//freopen("park.out","w",stdout);
int T;r(T);
rep(noip,1,T){
len=0;memset(fhead,0,sizeof(fhead));memset(zhead,0,sizeof(zhead));
r(n);r(m);r(k);r(p);
rep(i,1,m){int x,y,c;r(x);r(y);r(c);add_edge(x,y,c);}
spfa();
memset(dp,-1,sizeof(dp));
int ans=0;flag=true;
rep(i,0,k){
memset(hashs,0,sizeof(hashs));
ans=(ans+dfs(1,i))%p;
}
if(flag==false){w(-1);}
else {w(ans);}
}
return 0;
}
这么难的题让我这个将第一次参加NOIP提高组的小弱弱很慌啊。。。。
话说有没有人发现程序最上面的头文件注释中的时间是假的假的假的。。。。