传送门

注意数据中 k <= 50,考虑从这里入手解决问题

设 f [ i ] [ j ] 表示 已经到 i 点,最多还能比最短路程多走 j 的长度而不超过限制时的方案数

处理出终点到每个点的最短路程 dis [ i ]

那么对于一条边 (a,b,c), f [ a ] [ j ] --> f [ b ] [ j - (dis[a]-dis[b]+c) ]

那么对于每一个状态

 

P3953 逛公园_i++

 

如果直接DP顺序不好确定,所以考虑用记忆化搜索实现

然后考虑判断无穷多的路线

显然如果 dfs 时重复走到同一个状态说明出现了无穷多路线,所以开一个数组存一下当前走过的状态就好了

求最短路用的是Dijkstra

多组数据一定要记得清空

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=1e5+7,INF=0x3f3f3f3f;
int T,n,m,K,mo;
inline int fk(int x) { return x>=mo ? x-mo : x; }
int fir[N],from[N<<2],to[N<<2],val[N<<2],cntt;//存正图
inline void add(int &a,int &b,int &c)
{
    from[++cntt]=fir[a];
    fir[a]=cntt; to[cntt]=b; val[cntt]=c;
}
vector <int> v[N],g[N];//存反图
int dis[N];
struct data
{
    int pos,dis;
    inline bool operator < (const data &tmp) const {
        return dis>tmp.dis;
    }
};
priority_queue <data> q;
void Dijk()//Dijkstra不解释
{
    memset(dis,INF,sizeof(dis)); dis[n]=0; q.push((data){n,0});
    data x; int len;
    while(!q.empty())
    {
        x=q.top(); q.pop(); if(dis[x.pos]!=x.dis) continue;
        len=v[x.pos].size();
        for(int i=0;i<len;i++)
        {
            int u=v[x.pos][i],w=g[x.pos][i];
            if(dis[u]>x.dis+w) dis[u]=x.dis+w,q.push((data){u,dis[u]});
        }
    }
}
int f[N][57];
bool vis[N][57],flag;
void dfs(int x,int k)
{
    if(flag) return;
    vis[x][k]=1; if(x==n) f[x][k]=1;//f[n][k]初始为1
    //注意不能初始把所有f[n][k]=1,因为是记忆化,如果初始有值就不会dfs下去,而f[n][k]最终可能有不止一种方案
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i],w=k-(dis[v]-dis[x]+val[i]); if(w<0) continue;//判一下w是否符合限制
        if(vis[v][w])/*判断是否有无穷多的解*/ { flag=1; vis[x][k]=0; return; }//退出前记得vis=0
        if(!f[v][w]) dfs(v,w);
        f[x][k]=fk(f[x][k]+f[v][w]);//记忆化
    }
    vis[x][k]=0;
}
inline void clr()//初始化
{
    memset(f,0,sizeof(f)); memset(fir,0,sizeof(fir));
    for(int i=1;i<=n;i++) v[i].clear(),g[i].clear();
    cntt=flag=0;
}
int main()
{
    int a,b,c;
    T=read();
    while(T--)
    {
        n=read(); m=read(); K=read(); mo=read();
        clr();
        for(int i=1;i<=m;i++)
        {
            a=read(),b=read(),c=read();
            add(a,b,c); v[b].push_back(a); g[b].push_back(c);
        }
        Dijk();
        dfs(1,K);
        printf("%d\n",flag ? -1 : f[1][K]);//最终答案是f[1][K]
    }
    return 0;
}