洛谷P3953 [NOIP2017 提高组] 逛公园

@Pelom  October 12, 2021

题意

求长度$\le$最短路$+k$的路径数

数据范围:$n \le 100000,m \le 200000$

题解

首先需要跑一边最短路找到这个最短路;虽然保证存在$1 \rightarrow n$的路径,但并不是所有的点都能到$n$,所以应跑反向图
用$f[u][i]$表示从$u$到$n$的路径中$\le dis[u][n]+i$的路径数,则答案为$f[1][k]$
对于转移,考虑一条边$u \rightarrow v$,如果选择走这条边,那么到$n$的距离为$$dis[v][n]-dis[u][n]+w$$
那么,对于$u$的所有出边$$f[u][i]=\sum f[v][i-(dis[v][n]-dis[u][n]+w)]$$
题目中提到可能有无穷多条合法的路线,而简单路径的个数有限,那么在此指存在$0$环
对于$0$环的判断,记录一个$vis[u][i]$,当$vis[u][i]$再次被访问时即存在$0$环

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define mp make_pair
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int N=100000+10;
const int K=50+5;
int t,n,m,k,p,u,v,w;
int cnt;
int head1[N],head2[N],dis[N],dp[N][K];
bool vis[N][K];
struct edge{
    int nxt,to,w;
} e[N<<2];
inline void add(int *head,int u,int v,int w){
    e[++cnt]=edge{head[u],v,w};
    head[u]=cnt;
}
inline void Dijkstra(){
    priority_queue<pii,vector<pii>,greater<pii> > pq;
    memset(dis,0x3f,sizeof(dis));
    pq.push(mp(0,n));
    dis[n]=0;
    for(;!pq.empty();){
        int d=pq.top().first,u=pq.top().second;
        pq.pop();
        if(d!=dis[u])
            continue;
        for(int i=head2[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(dis[v]>d+e[i].w){
                dis[v]=d+e[i].w;
                pq.push(mp(dis[v],v));
            }
        }
    }
}
int dfs(int u,int k){
    if(vis[u][k])
        return -1;
    if(dp[u][k])
        return dp[u][k];
    vis[u][k]=1;
    dp[u][k]=(u==n);
    for(int i=head1[u];i;i=e[i].nxt){
        int v=e[i].to,d=dis[v]-dis[u]+e[i].w;
        if(d<=k){
            int t=dfs(v,k-d);
            if(t==-1)
                return dp[u][k]=-1;
            (dp[u][k]+=t)%=p;
        }
    }
    vis[u][k]=0;
    return dp[u][k];
}
int main(){
    scanf("%d",&t);
    for(;t--;){
        cnt=0;
        memset(head1,0,sizeof(head1));
        memset(head2,0,sizeof(head2));
        memset(vis,0,sizeof(vis));
        memset(dp,0,sizeof(dp));
        scanf("%d%d%d%d",&n,&m,&k,&p);
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w);
            add(head1,u,v,w);
            add(head2,v,u,w);
        }
        Dijkstra();
        printf("%d\n",dfs(1,k));
    }
    return 0;
}

添加新评论