JZOJ5475.【NOIP2017提高组】day1T3逛公园

33 篇文章 1 订阅
28 篇文章 0 订阅

problem

Description

   策策同学特别喜欢逛公园。公园可以看成一张��个点��条边构成的有向图,且没有自环和重边。其中1号点是公园的入口,��号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。
   策策每天都会去逛公园,他总是从1号点进去,从��号点出来。
   策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果1号点到��号点的最短路长为��,那么策策只会喜欢长度不超过�� + ��的路线。
   策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗?
   为避免输出过大,答案对��取模。
   如果有无穷多条合法的路线,请输出−1。

Input

输入文件名为 park.in 。
第一行包含一个整数 ��, 代表数据组数。
接下来��组数据,对于每组数据:
第一行包含四个整数 ��,��,��,��,每两个整数之间用一个空格隔开。
接下来��行,每行三个整数�� �� ,�� �� ,�� �� , 代表编号为�� �� ,�� �� 的点之间有一条权值为 �� �� 的有向边,每两个整数之间用一个空格隔开。

Output

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

Sample Input

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

Sample Output

3
-1

样例说明:
对于第一组数据,最短路为 3。
1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3 条合法路径。

Data Constraint

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

对于 100%的数据, 1 ≤ �� ≤ 10^ 9 ,1 ≤ �� �� ,�� �� ≤ �� ,0 ≤ �� �� ≤ 1000。


analysis

有人说这是防AK题?不算吧……(只不过我并没有做出来顶乱用)

首先用邻接表存边跑一遍spfa, dis[i] 表示1到i的最短路长度(不要和我说你不会spfa)
f[i][j] 表示从1到i的所有路径里,比 dis[n] 大K的路径条数
所以有

f[i][j]=f[k][dis[i]dis[k]+jlen[i][k]]

看到这个,你以为是 DP
记忆化搜索!
我们从 n 开始倒着搜索,若dis[i]dis[k]+jlen[i][k]0当然也就不搜索了

那么我们最大的敌人——判0环呢?
f[x][y] 这个状态在一遍dfs里出现两次,那就是有0环,return就好,开个数组标记一下

finally

ans=(i=0nf[n][i])modp


code

#include<stdio.h>
#include<cstring>
#define MAXN 100001
#define MAXM 200001

using namespace std;

int last[MAXM],next[MAXM],tov[MAXM],len[MAXM];
int Last[MAXM],Next[MAXM],Tov[MAXM],Len[MAXM];
int dis[MAXN],queue[10*MAXN];
int f[MAXN][51],c[MAXN][51];
int n,m,k,p,t,tot,ans;
bool bo,bz[MAXN];

void insert(int x,int y,int z)
{
    next[++tot]=last[x];
    last[x]=tot;
    tov[tot]=y;
    len[tot]=z;
    Next[tot]=Last[y];
    Last[y]=tot;
    Tov[tot]=x;
    Len[tot]=z;
}

int dfs(int x,int k)
{
    if(~f[x][k])return f[x][k];
    c[x][k]=1;
    f[x][k]=0;
    for(int i=Last[x];i;i=Next[i])
    {
        int j=Tov[i],t=dis[x]-dis[j]+k-Len[i];
        if(t<0)continue;
        if(c[j][t])bo=0;
        (f[x][k]+=dfs(j,t))%=p;
    }
    c[x][k]=0;
    return f[x][k];
}

int main()
{
    freopen("park.in","r",stdin);
    freopen("park.out","w",stdout);
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d%d%d",&n,&m,&k,&p);
        tot=0;
        memset(last,0,sizeof(last));
        memset(next,0,sizeof(next));
        memset(tov,0,sizeof(tov));
        memset(len,0,sizeof(len));
        memset(Last,0,sizeof(last));
        memset(Next,0,sizeof(next));
        memset(Tov,0,sizeof(tov));
        memset(Len,0,sizeof(len));
        memset(c,0,sizeof(c));
        memset(bz,1,sizeof(bz));
        memset(f,-1,sizeof(f));
        memset(dis,63,sizeof(dis));
        for (int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            insert(x,y,z);
        }
        int head=0,tail=1;
        bz[1]=dis[1]=ans=0;
        queue[1]=1;
        while (head!=tail)
        {
            head=(head+1==10*MAXN?0:head+1);
            int now=queue[head];
            for (int i=last[now];i;i=next[i])
            {
                int j=tov[i];
                if (dis[now]+len[i]<dis[j])
                {
                    dis[j]=dis[now]+len[i];
                    if (bz[j])
                    {
                        bz[j]=0;
                        tail=(tail+1==10*MAXN?0:tail+1);
                        queue[tail]=j;
                    }
                }
            }
            bz[now]=1;
        }
        bo=1;
        f[1][0]=1;
        for (int i=0;i<=k;i++)(ans+=dfs(n,i))%=p;
        dfs(n,k+1);
        if (!bo)
        {
            printf("-1\n");
        }
        else printf("%d\n",ans);
    }
    return 0;
}
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值