NOIP2017逛公园(dp+最短路)

时间:2022-12-31 15:02:27

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

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

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

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

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

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

Solution

看这像个分层图(其实也是分层图2333),首先一眼看到最短路计数,30pts到手。。

我们先把最短路DAG搞出来,拓扑一遍。

什么情况有无穷多解,有0环,我们拓扑完之后,如果还有点有入度,就是无解。

考虑到转移是和k相关的,而且是从小往大转移的,所以我们在枚举k之后,对于i->i转移的转移,我们按照拓扑序进行dp,对于i->i+k的转移,按照分层图的顺序转移就可以了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define N 100002
#define M 200002
#define mm make_pair
using namespace std;
queue<int>q1;
priority_queue<pair<int,int> >q; 
int dis[N],du[N],head[N],tot,n,t[N],tt,T,m,k,p,dp[N][52],ans,tttt;
bool vis[N];
struct fdd{
    int n,to,l;
}e[M];
inline void unit(){
    tot=0;
    memset(head,0,sizeof(head));
    memset(dp,0,sizeof(dp));tt=0;tttt=0;
    memset(du,0,sizeof(du));
}
inline void add(int u,int v,int l){
    e[++tot].n=head[u];
    e[tot].to=v;e[tot].l=l;
    head[u]=tot;
} 
inline void dij(int s){
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0;q.push(mm(0,s));
    while(!q.empty()){
        int u=q.top().second;q.pop();
        if(vis[u])continue;vis[u]=1;
        for(int i=head[u];i;i=e[i].n){
            int v=e[i].to;
            if(dis[v]>dis[u]+e[i].l){
                dis[v]=dis[u]+e[i].l;
                q.push(mm(-dis[v],v));
            }
        }
    }
}
inline void topo(){
    for(int i=1;i<=n;++i)if(!du[i])q1.push(i);
    while(!q1.empty()){
        int u=q1.front();q1.pop();t[++tt]=u;
        for(int i=head[u];i;i=e[i].n)if(dis[e[i].to]==dis[u]+e[i].l){
            int v=e[i].to;
            if(!--du[v])q1.push(v);
        }
    }
    for(int i=1;i<=n;++i)if(du[i])tttt=1;
}
inline int rd(){
    int x=0;char c=getchar();
    while(!isdigit(c))c=getchar();
    while(isdigit(c)){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x;
}
int main(){
    T=rd();
    while(T--){
        unit();
        int u,v,w;
        n=rd();m=rd();k=rd();p=rd();
        for(int i=1;i<=m;++i){
            u=rd();v=rd();w=rd();add(u,v,w);
        }
        dij(1);
        memset(vis,0,sizeof(vis));
        for(int u=1;u<=n;++u){
            for(int i=head[u];i;i=e[i].n){
                int v=e[i].to;
                if(dis[u]+e[i].l==dis[v])du[v]++;
            }
        }
        topo();
        if(tttt){
            printf("-1\n");
            continue;
        }
        dp[1][0]=1;
        for(int i=0;i<=k;++i){
            for(u=1;u<=tt;++u)
              for(int j=head[t[u]];j;j=e[j].n)if(dis[t[u]]+e[j].l==dis[e[j].to]){
                  int v=e[j].to;
                  (dp[v][i]+=dp[t[u]][i])%=p;
              }
            for(int u=1;u<=n;++u)
              for(int j=head[u];j;j=e[j].n)if(dis[u]+e[j].l!=dis[e[j].to]){
                  int v=e[j].to;
                  int kk=dis[u]-dis[v]+e[j].l+i;
                  if(kk<=k)(dp[v][kk]+=dp[u][i])%=p;
              }
        }
        ans=0;
        for(int i=0;i<=k;++i)(ans+=dp[n][i])%=p;
        printf("%d\n",ans);
    }
    return 0; 
}