[周末训练]逛公园

题目

【内存限制:$512MiB$】 【时间限制:$3000ms$】
【标准输入输出】 【题目类型:传统】 【评测方式:文本比较】

【题目描述】

策策同学特别喜欢逛公园。 公园可以看成一张$N$个点$M$条边构成的有向图,且没有自环和重边。其中$1$号点是公园的入口,$N$号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。

策策每天都会去逛公园,他总是从$1$号点进去,从$N$号点出来。

策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果$1$号点到$N$号点的最短路长为$d$,那么策策只会喜欢长度不超过$d+K$的路线。

策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗?

为避免输出过大,答案对$P$取模。

如果有无穷多条合法的路线,请输出$-1$

【输入格式】

第一行包含一个整数$T$, 代表数据组数。

接下来$T$组数据,对于每组数据:

第一行包含四个整数$N,M,K,P$, 每两个整数之间用一个空格隔开。

接下来$M$行,每行三个整数$a_i,b_i,c_i$, 代表编号为$a_i,b_i$的点之间有一条权值为$c_i$的有向边,每两个整数之间用一个空格隔开。

【输出格式】

输出文件包含$T$行,每行一个整数代表答案。

【样例】

样例输入

2
5 7 2 10
1 2 1
2 4 0
4 5 2
2 3 2
3 4 1
3 5 2
1 5 3
2 2 0 10
1 2 0
2 1 0

样例输出

3
-1

【样例解释】

对于第一组数据,最短路为$3$

$1-5,1-2-4-5,1-2-3-5$为$3$条合法路径。

【数据范围与提示】

对于不同测试点,我们约定各种参数的规模不会超过如下

对于 $100\%$ 的数据:$1\le P \le 10^9,1 \le a_i,b_i \le N ,0 \le c_i \le 1000$。
数据保证:至少存在一条合法的路线。

题解

【做题经历】

刚开始想的是一遍$dij$跑出最短路,然后对于每一条在最短路上的点,用其他的一些边来替换。

后来发现时间复杂度有点不可靠,大概是在$O(NM)$的范围左右。

【正解】

一道最短路+$DP$题。

先设最短路长度为$d$。

那么我们要求的就是长度在$d+K$以内的路径的数量。

首先,如果我们要把每一条路都求出来,那么这是一定不可行的,因为这道题的数据范围比较大。

那么我们应该怎么做这道题呢?

其实与我在做题的时候思路是比较靠近的,但是我想到的是将每一条边进行替换,却没有想到直接进行距离的计算。

我们先将输出$-1$的情况解决了。

其实很简单,就是在某一条合法路径上,有一个$0$环(就是一个环上所有的边权值都为$0$),那么就输出$-1$。

这个情况很简单,难点还是在计算总路径数上。

首先建一个逆图,从终点开始跑一边$dij$,得到每个点$u$到终点的最短距离$dis[u]$。

考虑边$edge[i]=(u,v,w)$,从$u$到$v$。
  • 如果$dis[u]+w=dis[v]$,说明$edge[i]$是存在于最短路径上的。
  • 如果$dis[u]+w≠dis[v]$,说明$edge[i]$是不存在于最短路径之上的,这个时候我们就要开始考虑替换,考虑有无其他情况。

现在开始处理第二种情况,如果$dis[u]+w-dis[v]≤K$,说明将从点$u$到点$v$的最短路径直接替换为$edge[i]$是合法的,因为这条新的路径与最短路的差即为从$u$到$v$的最短路径的差,而我们又保证了这个差$x$是小于等于$K$的。

当然,如果$dis[u]+w-dis[v]>K$,说明替换后的路径是不合法的,不用管即可。

但是如果我们将每次都存下来再计数,实际上是很费时间的,这个时候我们就要考虑记忆化(或者说是$DP$)。

令$dp[u][k]$:搜索到点$u$的一条路径上,其长度与路径长度上界(即$d+K$)的差值为$k$时,总路径的长度。

最后再整合一下代码即可。

顺带提一句,注意每组数据的初始化。不然你就$WA$穿地球了

#include<bits/stdc++.h>
using namespace std;
template<class T>inline void qread(T& x){
    char c;bool f=false;x=0;
    while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
    for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x; 
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
inline int rqread(){
    char c;bool f=false;int x=0;
    while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
    for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
const int MAXM=200000;
const int MAXN=100000;
const int MAXK=50;
class gragh{
    #define EDGE_SIZE 200000
    #define NODE_SIZE 100000
public:
    struct edge{int to,nxt,w;
        edge(){}
        edge(const int T,const int N,const int W):to(T),nxt(N),w(W){}
    }e[EDGE_SIZE+5];
    int tail[NODE_SIZE+5],ind;
    inline void add_edge(const int u,const int to,const int w){e[++ind]=edge(to,tail[u],w);tail[u]=ind;}
    inline void dijkstra(int dis[],const int s,const int N){
        struct node{int u,w;
            node(){}
            node(const int U,const int W):u(U),w(W){}
        };
        struct cmp{
            bool operator()(const node a,const node b){return !(a.w<b.w);}
        };
        for(int i=1;i<=N;++i)dis[i]=0x3f3f3f3f;
        priority_queue<node,vector<node>,cmp>Q;
        Q.push(node(s,dis[s]=0));
        while(!Q.empty()){
            int u=Q.top().u;Q.pop();
            for(int i=tail[u],v;i;i=e[i].nxt)if(dis[v=e[i].to]>dis[u]+e[i].w)
                Q.push(node(v,(dis[v]=dis[u]+e[i].w)));
        }
    }
    inline void clr(){memset(tail,ind=0,sizeof tail);}
    gragh(){clr();}
}G,rG;
int T,N,M,K,P,a,b,c;
int dis[MAXN+5],dp[MAXN+5][MAXK+5];
bool in[MAXN+5][MAXK+5];
int dfs(const int u,const int k){
    if(in[u][k])return -1;
    if(dp[u][k])return dp[u][k];
    in[u][k]=true;dp[u][k]=(u==N)?1:0;
    for(int i=G.tail[u],v,w,tmp;i;i=G.e[i].nxt)if((tmp=dis[v=G.e[i].to]-dis[u]+G.e[i].w)<=k){
        if((w=dfs(v,k-tmp))==-1)return dp[u][k]=-1;
        (dp[u][k]+=w)%=P;
    }
    return in[u][k]=false,dp[u][k];
}
void init(){
    memset(dp,0,sizeof dp);
    memset(in,false,sizeof in);
    G.clr(),rG.clr();
}
signed main(){
    qread(T);
    while(T--){
        init();
        qread(N,M,K,P);
        while(M--)qread(a,b,c),G.add_edge(a,b,c),rG.add_edge(b,a,c);
        rG.dijkstra(dis,N,N);
        printf("%d\n",dfs(1,K));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/MachineryCountry/p/11624547.html