NOIP 2017 逛公园
策策同学特别喜欢逛公园。公园可以看成一张N个点M条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从N号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到NN号点的最短路长为d,那么策策只会喜欢长度不超过d+K的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对P取模。
如果有无穷多条合法的路线,请输出−1。
去年的考试题,考场上没有想到,连DFS都打错了
首先来看k=0的情况,就是一个裸的最短路计数
然后是正解,我们有最短路计数可以想到正解肯定是个DP
dp[i][j]代表的是从1号点到i号点,满足需要比最短路多走j的长度的路线的数量
因为k的最大值是50,所以可以开状态
然后来看具体如何实现,我们先SPFA求出单源最短路
然后反向建图,这样可以避免走进死路
然后会发现DP直接写并不好实现,因为转移顺序不好确定
于是想到记忆化搜索,需要那个就转移
因为DP状态已经设好了
所以只需要转移,每一次我们对于每条边进行操作
对于边u->v 权值为w
我们可以通过刚才求出来的最短路来计算出从1到v还需要多走多长的路
也就是dis[u]-dis[v]+j-w j为当前还需要多走的长度 (注意,此时的u,v是在反向图中的)
然后求和即可
下面给出代码:(注意赋初值)
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<cmath> #include<algorithm> using namespace std; inline long long rd(){ long long x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void write(long long x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); return ; } long long n,m,k,mod; long long head[1000006]; long long nxt[2000006],to[2000006]; long long v[2000006]; long long total=0; void add(long long x,long long y,long long z){ total++; v[total]=z; to[total]=y; nxt[total]=head[x]; head[x]=total; return ; } long long q[1000006]; long long l=0,r=0; long long book[1000006]; long long dis[1000006]; void spfa(long long x){ memset(dis,127,sizeof(dis)); memset(book,0,sizeof(book)); l=0,r=0; book[x]=1; dis[x]=0; q[++r]=x; while(l<r){ long long h=q[++l]; book[h]=0; for(long long e=head[h];e;e=nxt[e]){ if(dis[to[e]]>dis[h]+v[e]){ dis[to[e]]=dis[h]+v[e]; if(!book[to[e]]){ q[++r]=to[e]; book[to[e]]=1; } } } book[h]=0; } return ; } long long f[100006][56]; long long vis[100006][56]; long long head2[200006]; long long to2[200006]; long long nxt2[200006]; long long v2[200006]; long long total2=0; void add2(long long x,long long y,long long z){ total2++; to2[total2]=y; v2[total2]=z; nxt2[total2]=head2[x]; head2[x]=total2; return ; } long long set=0; long long dfs(long long x,long long y){ if(f[x][y]!=-1) return f[x][y]; vis[x][y]=1; f[x][y]=0; for(long long e=head2[x];e;e=nxt2[e]){ long long h1=to2[e],h2=dis[x]-dis[to2[e]]-v2[e]+y; if(h2>=0){ if(vis[h1][h2]) set=1; int num=dfs(h1,h2); f[x][y]=(f[x][y]+num)%mod; } } vis[x][y]=0; return f[x][y]; } int main(){ long long T=rd(); while(T--){ memset(head,0,sizeof(head)); memset(head2,0,sizeof(head2)); total=total2=0; n=rd(),m=rd(),k=rd(),mod=rd(); for(long long i=1;i<=m;i++){ long long x=rd(),y=rd(),z=rd(); add(x,y,z),add2(y,x,z); } spfa(1); memset(f,-1,sizeof(f)); memset(vis,0,sizeof(vis)); f[1][0]=1; long long ans=0; set=0; for(long long i=0;i<=k;i++){ ans=(ans+dfs(n,i))%mod; } dfs(n,k+1); if(!set) write(ans%mod),puts(""); else write(-1),puts(""); } return 0; }
蒟蒻总是更懂你✿✿ヽ(°▽°)ノ✿