首页 > 其他分享 >P3953 [NOIP2017 提高组] 逛公园

P3953 [NOIP2017 提高组] 逛公园

时间:2023-10-07 19:56:00浏览次数:30  
标签:NOIP2017 return int rest 策策 逛公园 P3953 号点 dis

Description

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

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

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

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

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

\(N\le 10^5,M\le 2\times 10^5,K\le 50\)。

Solution

\(K\) 的范围较小,枚举 \(i\) 表示额外长度。

先求出 \(1\) 到每个点的最短路 \(dis\)。设 \(f_{x,v}\) 表示到节点 \(x\) 额外长度为 \(v\) 的方案数。在反向图上遍历。若点 \(x\) 可以从点 \(y\) 走到,则有转移 \(f_{x,v}=\sum f_{y,v-(dis_y+z-dis_x)}\)。其中 \(dis_y+z-dis_x\) 是走这条路所需要的额外长度。

当有 \(0\) 环就存在无穷多条合法路线。而有 \(0\) 环意味着可能在求 \(f_{x,v}\) 时又走回了 \(f_{x,v}\) 这个状态。因此可以记录每个状态 \(\{x,v\}\) 是否在待处理的栈中。若 \(\{x,v\}\) 二次入栈则说明存在 \(0\) 环。

Code

#include<queue>
#include<cstdio>
#include<cstring>
#define N 100005
#define M 200005
#define K 55
#define inf 0x3f3f3f3f
using namespace std;
int T,n,m,mod,k,tot,ans,dis[N],f[N][K];
bool bj[N],bz[N][K];
struct edg
{
    int tot=0,to[M],next[M],head[N],val[M];
    void add(int x,int y,int z) {to[++tot]=y;val[tot]=z;next[tot]=head[x];head[x]=tot;}
}a,_a;
struct node
{
    int x,dis;
    bool operator>(const node& x) const {return dis>x.dis;}
};
priority_queue<node,vector<node>,greater<node>> q;
void dij()
{
    memset(dis,inf,sizeof(dis));
    memset(bj,false,sizeof(bj));
    dis[1]=0;q.push((node){1,0});
    while (!q.empty())
    {
        int x=q.top().x;q.pop();
        if (bj[x]) continue;
        bj[x]=true;
        for (int i=a.head[x];i;i=a.next[i])
        {
            int y=a.to[i],z=a.val[i];
            if (dis[y]>dis[x]+z)
            {
                dis[y]=dis[x]+z;
                if (!bj[y]) q.push((node){y,dis[y]});
            }
        }
    }
}
int dfs(int x,int rest)
{
    if (rest<0||rest>k) return 0;
    if (bz[x][rest])//二次入栈
    {
        bz[x][rest]=false;
        return -1;
    }
    if (f[x][rest]!=-1) return f[x][rest];
    bz[x][rest]=true;//入栈
    int res=0;
    for (int i=_a.head[x];i;i=_a.next[i])
    {
        int y=_a.to[i],z=_a.val[i];
        int s=dfs(y,rest-(dis[y]+z-dis[x]));
        if (s==-1) {bz[x][rest]=false;return -1;}
        res=(res+s)%mod;
    }
    bz[x][rest]=false;//出栈
    if (x==1&&rest==0) res++;//走到了起点
    return f[x][rest]=res;
}
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d%d%d",&n,&m,&k,&mod);
        for (int i=1;i<=m;++i)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            a.add(x,y,z);
            _a.add(y,x,z);
        }
        dij();
        memset(f,-1,sizeof(f));
        memset(bz,false,sizeof(bz));
        bool flag=true;ans=0;
        for (int i=0;i<=k;++i)
        {
            int res=dfs(n,i);
            if (res!=-1) ans=(ans+res)%mod;
            else {flag=false;break;} 
        }
        if (!flag) printf("-1\n");
        else printf("%d\n",ans);
        a.tot=_a.tot=0;
        for (int i=1;i<=n;++i)
            a.head[i]=_a.head[i]=0;
    }
    return 0;
}

标签:NOIP2017,return,int,rest,策策,逛公园,P3953,号点,dis
From: https://www.cnblogs.com/Livingston/p/17747314.html

相关文章

  • P3956 [NOIP2017 普及组] 棋盘
    传送门P3956[NOIP2017普及组]棋盘不清楚曾师为什么把这个神奇的题目放在搜索\(search\)专栏,反正我用\(dijkstra\)水过去了,虽然\(dijkstra\)严格来说也是一种能够解决一般性最短路问题的算法。然后考虑这道题的建图。这道题来看首先是去除魔法的部分,一般地,任意一个点只......
  • NOIP2017提高组初赛易错题解析
     8.由四个不同的点构成的简单无向连通图的个数是()A.32 B.35 C.38 D.41错误原因:数重了正解:分情况计算,6条边的有1种,5条边的有C(6,1)=6种,4条边的有C(6,4)=15种,3条边,要分度数,2+2+1+1的有12种,3+1+1+1的有4种,共38种 10.若 f0​=0,f1​=1,fn+1​=(fn​+fn−1)/2​​,则随着......
  • 「NOIP2017 普及组」棋盘 题解
    前言一个绿题,风光啊QwQ题面传送门思路怎么走我们定义一个函数dfs(x,y,coin,can,color)x,y表示坐标,coin表示当前的金币数量,color表示当前坐标的颜色,can表示当前是否能施展魔法。再加一个mp数组记录颜色,-1表示无颜色,0表示红色,1表示黄色为什么不直接使用mp[x][y]获取颜......
  • 【题解】[NOIP2017 提高组] 逛公园
    题目描述:策策同学特别喜欢逛公园。公园可以看成一张\(N\)个点\(M\)条边构成的有向图,且没有自环和重边。其中\(1\)号点是公园的入口,\(N\)号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。策策每天都会去逛公园,他总是从\(1\)号点进去,从\(N\)号......
  • P3954 [NOIP2017 普及组] 成绩
    [NOIP2017普及组]成绩题目背景NOIP2017普及组T1题目描述牛牛最近学习了C++入门课程,这门课程的总成绩计算方法是:总成绩=作业成绩$\times20%+$小测成绩$×30%+$期末考试成绩$\times50%$牛牛想知道,这门课程自己最终能得到多少分。输入格式三个非负整数$A,B,C$,分别......
  • 算法刷题记录:[NOIP2017]图书管理员
    题目链接https://ac.nowcoder.com/acm/contest/19306/1050题目分析因为要求最小编号,并且该编号是以读者的编号结尾,这边直接排序+翻转,找开头的数。记录是因为看到某个大佬非常好的思路,直接对编号进行取模,就是末尾的数。如果想得到末尾的数,直接进行取模即可~~AC代码#include<......
  • NOIP2017普及组试题题解
    1.成绩原题:https://www.luogu.com.cn/problem/P3954代码:#include<bits/stdc++.h>#definelllonglongusingnamespacestd;inta,b,c;intmain(){ cin>>a>>b>>c; cout<<a/10*2+b/10*3+c/10*5; return0;}解题思路:因为数据保证a,b,c都是10的......
  • [NOIP2017 普及组] 跳房子
    这是一道很复杂有趣的题目题目描述跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。跳房子的游戏规则如下:在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个格子能得到的分数。玩家......
  • [洛谷P3959][NOIP2017提高组] 宝藏
    [NOIP2017提高组]宝藏题目描述参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了\(n\)个深埋在地下的宝藏屋,也给出了这\(n\)个宝藏屋之间可供开发的\(m\)条道......
  • [NOIP2017 普及组]跳房子 【题解】
    题目背景NOIP2017普及组T4题目描述跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。跳房子的游戏规则如下:在地面上确定一个起点,然后在起点......