题面

传送门:https://www.luogu.org/problemnew/show/P3953


Solution

这是一道神题

首先,我们不妨想一下K=0,即求最短路方案数的部分分。

我们很容易可以想到一个做法,就是魔改迪杰斯特拉做法:

如果一个点可以更新到达其他点的距离,那个点的方案数就是这个点的方案数;如果一个点所更新出来的距离和之前的相等,那个点的方案数加等当前点的方案数。

用式子可以表现为:

f[j]=f[i] (dis[j]>dis[i]+x)  

f[j]+=f[i] (dis[j]==dis[i]+x)

(i表示当前点,j表示它更新的点,x为i到j那条路的距离)

那我们怎么保证它的顺序不会出错,即如何保证一个点去更新其他点的方案数的时候,这个点的方案数是正确的呢?

事实上,这种做法就是一种DP。

那么,对于K!=0的情况怎么处理呢?

观察数据,我们会发现K最大只有50。

因此,我们可以考虑在DP上加一维来解决这个K值。

考虑这样设状态:

f[i][j] 表示到达i点,距离为dis[i]+j 的方案数

转移非常好写

f[i][j] = sigema (f[k][dis[i]+j-dis[k]-a]) (k为直接连到i的点,a表示它们之间的边权)

初始化其实我们在30分做法中就已经求好了。

转移顺序是个问题。

我们显然可以在外层枚举j,问题是,有时候,dis[i]+j-dis[k]-a会等于j,如果枚举i的顺序错了,答案肯定会跟着错。

对于dis[i]+j-dis[k]-a==j 的点,肯定是k去更新i,又因为边权没有负值,所以我们就可以按照dis从小到大去去枚举i的值。

以上是没有零边的做法。

对于有零边的情况,我们刚刚的做法就会出问题。如图:

所以说,我们把原图转换一下,只保留0边,对新图做拓扑排序。

如果做完拓扑排序之后,有几个点没有进入过排序中,就说明这个图有零环,就gg了。

我们把拓扑序做完之后再执行原来有的最短路和dp,这样就不会错了。

就酱,我们就嘴巴AC这道题啦o(* ̄▽ ̄*)o 。

事实上,这样做并A不了,因为这题TM卡常(╯°Д°)╯︵┻━┻

然后,你会被卡30分并因此退役(或者是开O2A掉这道题(但是NOIP中并不开O2,所以你还是因此退役了))


Code

//Luogu P3953 逛公园
//Sep,18th,2018
//最短路+拓扑排序+DP+卡常神题
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
long long read()
{
long long x=0,f=1; char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int N=100000+100;
const int M=50+5;
const int inf=0x3f3f3f3f;
struct DIS
{
int dis,no,zero;
}dis[N];
bool cmp(DIS A,DIS B)
{
if(A.dis==B.dis)
return A.zero<B.zero;
return A.dis<B.dis;
}
struct road
{
int to,w;
road(int A,int B)
{
to=A,w=B;
}
friend bool operator < (road A,road B)
{
if(A.w==B.w)
return dis[A.to].zero > dis[B.to].zero;
return A.w > B.w;
}
};
vector <road> e[N],rev[N];
int n,m,K,poi,T,rd[N],dl2[N],front2,tail2,dis2[N];
long long f[N][M];
priority_queue <road,vector<road> > dl;
bool vis[N];
void dj()
{
for(int i=1;i<=n;i++)
dis[i].dis=inf,dis[i].no=i;
dis[1].dis=0;
memset(dis2,0x3f,sizeof dis2);
dis2[1]=0;
while(dl.empty()==false) dl.pop();
memset(vis,0,sizeof vis);
dl.push(road(1,0));
f[1][0]=1;
int cnt=0;
while(cnt!=n and dl.empty()==false)
{
road temp=dl.top();
dl.pop();
if(vis[temp.to]==true) continue;
vis[temp.to]=true;
int now=temp.to,dis_now=temp.w;
for(int i=0;i<int(e[now].size());i++)
if(dis_now+e[now][i].w < dis[e[now][i].to].dis)
{
f[e[now][i].to][0]=f[now][0];
dis[e[now][i].to].dis=dis_now+e[now][i].w;
dis2[e[now][i].to]=dis_now+e[now][i].w;
dl.push(road(e[now][i].to,dis[e[now][i].to].dis));
}
else if(dis_now+e[now][i].w == dis[e[now][i].to].dis)
f[e[now][i].to][0]=(f[e[now][i].to][0]+f[now][0])%poi;
}
}
void GetTP()
{
tail2=front2=0;
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++)
if(rd[i]==0)
dl2[tail2++]=i;
int cnt=0;
while(tail2>front2)
{
dis[dl2[front2]].zero=++cnt;
vis[dl2[front2]]=true;
for(int i=0;i<int(e[dl2[front2]].size());i++)
if(e[dl2[front2]][i].w==0 and vis[e[dl2[front2]][i].to]==false)
{
rd[e[dl2[front2]][i].to]--;
if(rd[e[dl2[front2]][i].to]==0)
dl2[tail2++]=e[dl2[front2]][i].to;
}
front2++;
}
}
int main()
{
T=read();
for(int i=1;i<N;i++)
e[i].reserve(4),rev[i].reserve(4);
for(;T>0;T--)
{
memset(f,0,sizeof f);
memset(rd,0,sizeof rd);
n=read(),m=read(),K=read(),poi=read();
for(int i=1;i<=n;i++)
e[i].clear(),rev[i].clear();
for(int i=1;i<=m;i++)
{
int a=read(),b=read(),c=read();
e[a].push_back(road(b,c));
rev[b].push_back(road(a,c));
if(c==0)
rd[b]++;
} GetTP();
bool OK=true;
for(int i=1;i<=n;i++)
if(vis[i]==false)
OK=false;
if(OK==false)
{
printf("-1\n");
continue;
}
dj(); sort(dis+1,dis+1+n,cmp);
for(int j=1;j<=K;j++)
for(int i=1;i<=n;i++)
for(int k=0;k<int(rev[dis[i].no].size());k++)
{
int t=dis[i].no,s=rev[t][k].to;
if(dis2[t]!=inf and dis2[s]!=inf and dis2[t]+j-dis2[s]-rev[t][k].w>=0 )
f[t][j]=(f[t][j]+f[s][dis2[t]+j-dis2[s]-rev[t][k].w])%poi;
} long long ans=0;
for(int i=0;i<=K;i++)
ans=(ans+f[n][i])%poi;
printf("%lld\n",ans);
}
return 0;
}

[Luogu P3953] 逛公园 (最短路+拓扑排序+DP)的更多相关文章

  1. [NOIP2017]逛公园 最短路+拓扑排序+dp

    题目描述 给出一张 $n$ 个点 $m$ 条边的有向图,边权为非负整数.求满足路径长度小于等于 $1$ 到 $n$ 最短路 $+k$ 的 $1$ 到 $n$ 的路径条数模 $p$ ,如果有无数条则输出 ...

  2. Luogu P3953 逛公园(最短路+记忆化搜索)

    P3953 逛公园 题面 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 \(N\) 个点 \(M\) 条边构成的有向图,且没有自环和重边.其中 \(1\) 号点是公园的入口,\(N\) 号点是公 ...

  3. 【BZOJ5109】[CodePlus 2017]大吉大利,晚上吃鸡! 最短路+拓扑排序+DP

    [BZOJ5109][CodePlus 2017]大吉大利,晚上吃鸡! Description 最近<绝地求生:大逃杀>风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏.在游戏 ...

  4. NOIP2017 Day1 T3 逛公园(最短路+拓扑排序+DP)

    神tm比赛时多清个零就有60了T T 首先跑出1起点和n起点的最短路,因为k只有50,所以可以DP.设f[i][j]表示比最短路多走i的长度,到j的方案数. 我们发现如果在最短路上的和零边会有后向性, ...

  5. Luogu P3953 逛公园

    不管怎么说,这都是一道十分神仙的NOIp题 你可以说它狗,但不可以否认它就是NOIp的难度 首先这道题很显然是道图论题还是一道图论三合一(最短路+拓扑+图上DP) 先考虑最短路,我们分别以\(1\)和 ...

  6. 【luogu P3953 逛公园】 题解

    题目链接:https://www.luogu.org/problemnew/show/P3953 题外话:感觉2017年神题好多..这还不是最神的一道,真在考场上我也就写个最短路计数暴力了.现在在大佬 ...

  7. P3953 逛公园(dp,最短路)

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

  8. [LOJ 3101] [Luogu 5332] [JSOI2019]精准预测(2-SAT+拓扑排序+bitset)

    [LOJ 3101] [Luogu 5332] [JSOI2019]精准预测(2-SAT+拓扑排序+bitset) 题面 题面较长,略 分析 首先,发现火星人只有死和活两种状态,考虑2-SAT 建图 ...

  9. BZOJ_1916_[Usaco2010 Open]冲浪_分层图+拓扑排序+DP

    BZOJ_1916_[Usaco2010 Open]冲浪_分层图+拓扑排序+DP Description 受到秘鲁的马丘比丘的新式水上乐园的启发,Farmer John决定也为奶牛们建 一个水上乐园. ...

随机推荐

  1. mysqldump 5.7

    简介 mysqldump是官方自带的文本格式备份工具,简单实用,一般在Mysql安装目录的bin目录下.备份文件默认是SQL格式,它由一系列语句例如CREATE TABLE.INSERT等组成.mys ...

  2. Centos-移动文件或目录-mv

    mv 移动文件或者目录,可以用重命名文件或者目录 相关选项 -i 如果文件或目录存在询问是否覆盖,输入y确认,输入n取消 -f 不提示,覆盖

  3. C++读写ini配置文件GetPrivateProfileString()&WritePrivateProfileString()

    转载: 1.https://blog.csdn.net/fengbingchun/article/details/6075716 2. 转自:http://hi.baidu.com/andywangc ...

  4. 插头 dp

    插头dp 洛谷 黑题板子? P5056 给出n×m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路.问有多少种铺法? 1.轮廓线 简单地说,轮廓线就是已决策格子和未决策格子的分界线: 2,插 ...

  5. 手写一个类SpringBoot的HTTP框架:几十行代码基于Netty搭建一个 HTTP Server

    本文已经收录进 : https://github.com/Snailclimb/netty-practical-tutorial (Netty 从入门到实战:手写 HTTP Server+RPC 框架 ...

  6. VS Code 搭建编写Shell环境(WSL)

    安装过程 Win10开启WSL,方法略 安装VSCode,方法略 安装语法提示插件:shellman 安装格式化插件:shell-format(右键 -> 格式化文档(Ctrl + Alt + ...

  7. Python 自动化测试全攻略:五种自动化测试模型实战详解

    随着移动互联网的发展,软件研发模型逐步完善,软件交付质量越来越受到软件公司的重视,软件测试技术特别是自动化测试技术开始在软件系统研发过程中发挥着越来越重要的作用. 与传统的手工测试技术相比,自动化测试 ...

  8. 这个网易云JS解密,老网抑云看了都直呼内行

    最近更新频率慢了,这不是因为CK3发售了嘛,一个字就是"肝".今天来看一下网易云音乐两个加密参数params和encSecKey,顺便抓取一波某歌单的粉丝,有入库哦,使用mysql ...

  9. xshell选项卡不见了

    最近使用xshell的时候发现建立多个选项卡的时候,因为没有选项卡,所以不能切换. 弄了好一会儿才弄出来 点击会话选项卡或者Ctrl + Shift + T可以调出来

  10. python实现自动生成小学四则运算题目(软工第二次项目作业)

    前言 软件工程 传送带 作业要求 传送带 作业目标 结对编程:代码实现.性能分析.异常处理说明.记录PSP表格 代码见: github 个人信息:朱育清 3118005437 信安二班 我的partn ...