park作为今年noipday1最后一道题还是相比前面几道题还是有点难度的

首先你可以思考一下,第一天dp不见了,再看一下这题,有向图,看起来就比较像一个dp,考虑dp方程,首先肯定有一维是到哪个节点,还有一维肯定与路径长度有关,显然第二位就记录超过最短路多少。

这样我们可以找到dp方程,首先枚举一个kk(0<=kk<=k),按拓扑序枚举每一个点,枚举以这个点为起点的路径,如果这条路在最短路上,那么dp[v][kk]+=dp[u][kk],else 如果dis[u]+kk+路径长度<=dis[v]+k则也可以进行类似的转移

首先我们先正着跑一遍spfa,再反着跑一遍,以为要判断一个点在不在最短路上,只需要看起点到它的最短路+终点到它的最短路之和是否等于起点到终点的最短路

接下来如果这条边在最短路上则这条路终点入度+1,再拓扑排序一遍顺便检查出有无0环,接下来就可以dp了

接下来看代码吧

#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=1e5+5;
bool cmax(sign &a,sign b){return a<b?a=b,1:0;}
bool cmin(sign &a,sign b){return a>b?a=b,1:0;}
template<typename T>T read()
{
T ans=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
return ans*f;
}
void file()
{
#ifndef ONLINE_JUDGE
freopen("park.in","r",stdin);
freopen("park.out","w",stdout);
#endif
}
int dis[N][2],in[N];
int n,m,k,p;
struct graph
{
int head[N],tt,nex[N<<1],w[N<<1],to[N<<1];
inline void init()
{
tt=0;
For(i,1,n)head[i]=0;
}
inline void add(int x,int y,int z)
{
++tt;
w[tt]=z;to[tt]=y;
nex[tt]=head[x];head[x]=tt;
}
}G[2];//因为双向建图,所以写在结构体里
#define tra(G,i,u) for(register int i=G.head[u];i;i=G.nex[i])
bool vis[N];
inline void input()
{
int x,y,z;
n=read<int>();m=read<int>();k=read<int>();p=read<int>();
G[0].init();G[1].init();
For(i,1,m)
{
x=read<int>();y=read<int>();z=read<int>();
G[0].add(x,y,z);G[1].add(y,x,z);
}
}
queue<int>q;
inline void spfa(int st,int now)
{
q.push(st);
dis[st][now]=0;
int u,v;
while(!q.empty())
{
u=q.front();q.pop();
vis[u]=0;
tra(G[now],i,u)
{
v=G[now].to[i];
if(cmin(dis[v][now],dis[u][now]+G[now].w[i]))
{
if(!vis[v])
{
vis[v]=1;
//if(!q.empty()&&dis[v]<=dis[q.front()])q.push_front(v);
q.push(v);
}
}
}
}
}//跑spfa,因为要跑两遍,所以0表示正着的,1表示反着的
int l[N];
bool check()
{
int v,u;
For(i,1,n)if(!in[i])l[++l[0]]=i;//入度为零入队
For(j,1,l[0])
{
u=l[j];
tra(G[0],i,u)
{
v=G[0].to[i];
if(dis[v][0]==dis[u][0]+G[0].w[i])
{
//在最短路上就入度-1
--in[v];
if(!in[v])l[++l[0]]=v;
}
}
}
For(i,1,n)if(in[i]&&dis[i][0]+dis[i][1]<=dis[n][0]+k)return 1;
//如果在最短路上且有环,则说明有0环
return 0;
}
int dp[N][55];
inline void add(int &a,int b)
{
a+=b;
if(a>p)a-=p;
}
inline void topsort()
{
int v,u;
dp[1][0]=1;
For(kk,0,k)
{
For(j,1,l[0])
{
u=l[j];
tra(G[0],i,u)
{
v=G[0].to[i];
if(dis[v][0]==dis[u][0]+G[0].w[i])add(dp[v][kk],dp[u][kk]);//如果在最短路上,则沿着最短路走下去,当前比最短路大kk,则走到终点也一定比最短路大kk
else if(kk+dis[u][0]+G[0].w[i]<=dis[v][0]+k)
{
add(dp[v][kk+dis[u][0]+G[0].w[i]-dis[v][0]],dp[u][kk]);//走过这条路,当前比最短路大kk,则走过这条路后,又长了dis[u][0]+G[0].w[i]-dis[v][0]]
}
}
}
}
int ans=0;
For(i,0,k)add(ans,dp[n][i]);
printf("%d\n",ans);
}
inline void work()
{
spfa(1,0);
spfa(n,1);
int v;l[0]=0;
For(u,1,n)
{
tra(G[0],i,u)
{
v=G[0].to[i];
if(dis[v][0]==dis[u][0]+G[0].w[i])in[v]++;//入度
}
}
if(check())
{
puts("-1");
return;
}
topsort();
}
const int inf=0x3f3f3f3f;
inline void init()
{
For(i,1,n)For(j,0,1)dis[i][j]=inf;
For(i,1,n)in[i]=0;
For(i,1,n)For(j,0,k)dp[i][j]=0;
//初始化,写memset,在洛谷一直re
}
int main()
{
//从main函数看起是个好习惯
file();
int T=read<int>();
while(T--)
{
input();
init();
work();
}
return 0;
}

NOIP2017逛公园(park)解题报告的更多相关文章

  1. 【比赛】NOIP2017 逛公园

    考试的时候灵光一闪,瞬间推出DP方程,但是不知道怎么判-1,然后?然后就炸了. 后来发现,我只要把拓扑和DP分开,中间加一个判断,就AC了,可惜. 看这道题,我们首先来想有哪些情况是-1:只要有零环在 ...

  2. [NOIP2017] 逛公园

    [NOIP2017] 逛公园 题目大意: 给定一张图,询问长度 不超过1到n的最短路长度加k 的1到n的路径 有多少条. 数据范围: 点数\(n \le 10^5\) ,边数\(m \le 2*10^ ...

  3. 【题解】NOIP2017逛公园(DP)

    [题解]NOIP2017逛公园(DP) 第一次交挂了27分...我是不是必将惨败了... 考虑这样一种做法,设\(d_i\)表示从该节点到n​节点的最短路径,\(dp(i,k)\)表示从\(i\)节点 ...

  4. NOIP2017 逛公园 题解报告 【最短路 + 拓扑序 + dp】

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

  5. NOIP2017逛公园(dp+最短路)

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

  6. 冲刺Noip2017模拟赛5 解题报告——五十岚芒果酱

    1. 公约数(gcd) [问题描述] 给定一个正整数,在[,n]的范围内,求出有多少个无序数对(a,b)满足 gcd(a,b)=a xor b. [输入格式] 输入共一行,一个正整数n. [输出格式] ...

  7. 冲刺Noip2017模拟赛3 解题报告——五十岚芒果酱

    题1  素数 [问题描述] 给定一个正整数N,询问1到N中有多少个素数. [输入格式]primenum.in 一个正整数N. [输出格式]primenum.out 一个数Ans,表示1到N中有多少个素 ...

  8. 冲刺Noip2017模拟赛2 解题报告——五十岚芒果酱

    题1 牛跑步(running) [题目描述] 新牛到部队,CG 要求它们每天早上搞晨跑,从 A 农场跑到 B 农场.从 A 农场到 B 农场中有 n- 个路口,分别标上号,A 农场为 号,B 农场为 ...

  9. 冲刺Noip2017模拟赛1 解题报告——五十岚芒果酱

    题1 国际象棋(chess) [问题描述] 有N个人要参加国际象棋比赛,该比赛要进行K场对弈.每个人最多参加2场对弈,最少参加0场对弈.每个人都有一个与其他人都不相同的等级(用一个正整数来表示).在对 ...

  10. [NOIP2017]逛公园 题解

    我连D1T3都不会我联赛完蛋了 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口, N 号点是公园的出口,每条边有一个非负 ...

随机推荐

  1. Linux服务-openssh

    目录 1. 使用 SSH 访问远程命令行 1.1 OpenSSH 简介 1.2 SSH 版本 1.3 SSH 认证方式 1.4 openSSH 的工作模式 1.5 Secure Shell 示例 1. ...

  2. 20155210 Exp2 后门原理与实践

    20155210 Exp2 后门原理与实践 1.Windows获得Linux Shell 在windows下,打开CMD,使用ipconfig指令查看本机IP 如图: 然后使用ncat.exe程序,n ...

  3. WPF编程,通过Double Animation动态旋转控件的一种方法。

    原文:WPF编程,通过Double Animation动态旋转控件的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/art ...

  4. 汇编  cdecl 函数调用约定,stdcall 函数调用约定

    知识点:  cdecl 函数调用约定  stdcall 函数调用约定  CALL堆栈平衡 配置属性--> c/c++ -->高级-->调用约定 一.cdecl调用约定 VC++ ...

  5. mfc CFileFind查找类

    查找文件 CFileFind类 提取文件图标 显示大图标 显示小图标 一.查找文件 . CFileFind类 //c:\mydir\myfile.txt GetFileName 获取文件名 myfil ...

  6. STM32通用定时器原理

    /************************************************************************************************ 转载 ...

  7. python 相对路径导入 与 绝对路径导入

    我的理解: 假设有一个文件夹 app 若 app 下有app/__init__.py文件,则此 app 被视作一个 package,而 app 下的其他文件/文件夹被视作 module 我们知道,pa ...

  8. 洛咕 P4474 王者之剑

    宝石只能在偶数秒取到,假设有一个宝石在奇数秒取到了,那么上一秒是偶数秒,在上一秒的时候这里的宝石就没了. 相邻的两个宝石不能同时取,很显然,先取一块,那么这是偶数秒,取完了这一块之后相邻的都没了. 只 ...

  9. Linux每天一个命令:cat

    Linux cat命令 命令:cat cat 命令用于连接文件并打印到标准输出设备上. 使用权限 所有使用者 语法格式 cat [-AbeEnstTuv] [--help] [--version] f ...

  10. nvm管理不同版本的node和npm

    写在前面 nvm(nodejs version manager)是nodejs的管理工具,如果你需要快速更新node版本,并且不覆盖之前的版本:或者想要在不同的node版本之间进行切换:使用nvm来安 ...