【noip2017】逛公园

题目描述

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

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

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

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

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

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


输入

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

接下来 T 组数据,对于每组数据: 第一行包含四个整数 N,M,K,P ,每两个整数之间用一个空格隔开。

接下来 M 行,每行三个整数 ai,bi,ci ,代表编号为 ai,bi 的点之间有一条权值为 ci 的有向边,每两个整数之间用一个空格隔开(可能有0边)。

对于 100%的数据, 1≤ P ≤ 109,N ≤ 100000 ,M ≤ 200000 ,1 ≤ ai,bi ≤ N,0 ≤ ci ≤ 1000。


输出

输出文件包含 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


题解

这就是DAY1 的dp了。

首先很容易想到先spfa求一遍最短路。接下来我们要反向建图,再做一遍spfa求出终点 n 到各点的最短路 d[ i ] ,于是在每个点我们都可以知道它离终点的最短距离,然后当我们从点 i 往下一个点 j 走的时候,如果d[ j ] + dis[ i ][ j ] > d[ i ] ,说明我们走了冤枉路,多走的冤枉路的值是 d[ j ] + dis[ i ][ j ] - d[ i ] 。因为我们不能选择大于最短路+k 的长度的路径,所以我们走的冤枉路的最大值就是 k,于是很容易的就想到了dp。

用 dp[ i ][ j ] 表示在 i 号点还可以允许走 j 的冤枉路的方案数,考虑向下一个点转移,转移方程为: dp[ i ][ j ] = Σ dp[ j ][   j - ( dis[ i ][ j ] + d[ j ] - d[ i ] )   ]   其中 j 是与 i 相邻的点。

什么情况输出“-1”呢?题目中说有 0 边,那么当出现 0 环的时候就有无穷多的情况了,只要判掉这种情况即可。

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long

const int maxn=100000+5;
const int maxm=200000+5;
const int inf=0x7FFFFFFF;

int n,m,k,T,pp,a,b,cc,dp[maxn][60],c[maxn],d[maxn];
int fir[maxn],to[maxm],nex[maxm],wi[maxm],ecnt;
int fi[maxn],t[maxm],ne[maxm],w[maxm],e,q[1000001];
bool p[maxn],u[maxn],v[maxn][60];

template<typename T>void read(T& aa) {
    char cc; ll ff;aa=0;cc=getchar();ff=1;
    while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    if(cc=='-') ff=-1,cc=getchar();
    while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    aa*=ff;
}

void add_edge(int u,int v,int ww){
    nex[++ecnt]=fir[u];fir[u]=ecnt;to[ecnt]=v;wi[ecnt]=ww;
    ne[++e]=fi[v];fi[v]=e;t[e]=u;w[e]=ww;
}

void spfa1(int x){
    memset(p,false,sizeof(p));
    memset(c,127,sizeof(c));
    p[x]=true;
    c[x]=0;
    q[1]=x;
    int head=0,tail=1;
    while(head<tail){
        head++;
        int u=q[head];
        p[u]=false;
        for(int e=fir[u];e;e=nex[e]){
            int vv=to[e];
            if(c[u]+wi[e]<c[vv]){
                c[vv]=c[u]+wi[e];
                if(!p[vv]){
                    tail++;
                    q[tail]=vv;
                    p[vv]=true;
                }
            }
        }
    }
}

void spfa2(int x){
    memset(p,false,sizeof(p));
    memset(d,127,sizeof(d));
    p[x]=true;
    d[x]=0;
    q[1]=x;
    int head=0,tail=1;
    while(head<tail){
        head++;
        int u=q[head];
        p[u]=false;
        for(int e=fi[u];e;e=ne[e]){
            int vv=t[e];
            if(d[u]+w[e]<d[vv]){
                d[vv]=d[u]+w[e];
                if(!p[vv]){
                    tail++;
                    q[tail]=vv;
                    p[vv]=true;
                }
            }
        }
    }
}

#define mem(a,b) memset(a,b,sizeof(a))
void init(){
    mem(fir,0);mem(nex,0);mem(to,0);mem(wi,0);ecnt=0;
    mem(fi,0);mem(ne,0);mem(t,0);mem(w,0);e=0;
    mem(u,0);mem(dp,-1);mem(v,0);
}

int dfs(int a,int b){
    if(b<0) return 0;
    else if(v[a][b]) return -inf;
    else if(dp[a][b]!=-1) return dp[a][b];
    else{
        v[a][b]=1;
        int key=0;
        if(a==n) key++;
        for(int i=fir[a];i;i=nex[i]){
            int vv=to[i];
            if(!u[vv]) continue;
            int ww=dfs(vv,b-(wi[i]+d[vv]-d[a]));
            if(ww==-inf) return -inf;
            key=(key+ww)%pp;
        }
        dp[a][b]=key%pp;
        v[a][b]=0;
        return key;
    }
}

int main(){
    read(T);
    while(T--){
        init();
        read(n),read(m),read(k),read(pp);
        for(int i=1;i<=m;i++){
            read(a),read(b),read(cc);
            add_edge(a,b,cc);
        }
        spfa1(1);
        spfa2(n);
        for(int i=1;i<=n;i++)if(d[i]!=2139062143) u[i]=1;
        int z=dfs(1,k);
        if(z==-inf) printf("-1\n");
        else printf("%d\n",z);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/rlddd/p/9502351.html