[NOIP2017]逛公园

Posted faced

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOIP2017]逛公园相关的知识,希望对你有一定的参考价值。

[NOIP2017]逛公园https://www.luogu.org/problemnew/show/P3953

题目描述

策策同学特别喜欢逛公园。公园可以看成一张 \(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≤P≤10^9,1≤a_i,b_i≤N,0≤c_i≤1000$
数据保证:至少存在一条合法的路线。


建反图记忆化搜索
先一边\(Spfa()\)预处理出最短路\(dist[i]\)
\(dp[i][j]\)表示到点\(i\)距离不超过\(dist[i]+j\)的路径条数
有:
\[dp[1][0]=1\]
对于一条反边\((u,v,w)\)
\[dp[u][k]=\sum dp[v][dist[u]+k-w-dist[v]]\]
判零环:
如果一个未确定的\(dp\)值被经过两次,显然有零环,标记数组\(c[i][j]\)记录即可

#define RG register
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=1e5+5,M=2e5+5;
inline int read()
{
    RG int x=0,w=1;RG char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar();
    return x*w;
}
int T,n,K,m,p,cnt,Ans;
int last[N],Last[N],dist[N],f[N][55];
bool Flag;
bool used[N],c[N][55];
struct edge{int to,next,w;}e[M],E[M];
queue<int> Q;
inline void insert(int u,int v,int w)
{
    cnt++;
    e[cnt]=(edge){v,last[u],w};last[u]=cnt;
    E[cnt]=(edge){u,Last[v],w};Last[v]=cnt;
}
inline void init()
{
    cnt=0;Ans=0;Flag=false;
    memset(last,0,sizeof(last));
    memset(Last,0,sizeof(Last));
    memset(dist,63,sizeof(dist));
    memset(used,false,sizeof(used));
    memset(f,-1,sizeof(f));
    memset(c,false,sizeof(c));
    memset(e,0,sizeof(e));
    memset(E,0,sizeof(E));
}
inline void Spfa()
{
    while(!Q.empty())Q.pop();
    Q.push(1);
    used[1]=true;dist[1]=0;
    while(!Q.empty())
    {
        int now=Q.front();Q.pop();
        used[now]=false;
        for(RG int i=last[now];i;i=e[i].next)
        {
            int v=e[i].to;
            if(dist[v]>dist[now]+e[i].w)
            {
                dist[v]=dist[now]+e[i].w;
                if(!used[v])Q.push(v),used[v]=true;
            }
        }
    }
}
int dfs(int now,int k)
{
    if(~f[now][k])return f[now][k];
    c[now][k]=true;
    f[now][k]=0;
    for(RG int i=Last[now];i;i=E[i].next)
    {
        int v=E[i].to;
        int t=dist[now]+k-E[i].w-dist[v];
        if(t<0)continue;
        if(c[v][t])Flag=true;
        f[now][k]+=dfs(v,t);f[now][k]%=p;
    }
    c[now][k]=false;
    return f[now][k];
}
int main()
{
    T=read();
    while(T--)
    {
        init();
        n=read(),m=read(),K=read(),p=read();
        RG int u,v,w;
        for(RG int i=1;i<=m;i++)
        {
            u=read(),v=read(),w=read();
            insert(u,v,w);
        }
        f[1][0]=1;
        Spfa();
        for(RG int i=0;i<=K;i++)Ans+=dfs(n,i),Ans%=p;
        dfs(n,K+1);//判零环[K=0]
        if(Flag){puts("-1");continue;}
        printf("%d\n",Ans);
    }
    return 0;
}

以上是关于[NOIP2017]逛公园的主要内容,如果未能解决你的问题,请参考以下文章

Noip2017 逛公园

[NOIP2017] 逛公园

[luogu P3953] [noip2017 d1t3] 逛公园

[Noip2017]逛公园

Luogu P3953NOIP2017逛公园最短路+拓扑排序+动态规划

P3953 NOIP2017 d1t3 逛公园