LINK

思路

因为我想到的根本不是网上的普遍做法

所以常数出奇的大,而且做法极其暴力

可以形容是带优化的大模拟

进入正题:

首先一个很显然的思路是如果在合法的路径网络里面存在零环是有无数组解的

然后这个直接对所有边权是0的边进行一次toposort看看有没有点没有被访问到

然后剩下的dp怎么设计?

\(dp_{i,j}\)表示走到了第i个点,如果当前点到n走最短路最后路径比最短路径多出来了j

然后转移的时候发现是需要搞定顺序的问题?咋办?

发现一条边新的贡献是\(dis2_{v}+E[i].w-dis2_u\),其中dis2是反图跑出来的最短路

然后就可以把每条边的权值换一下

发现这个时候我们要先枚举j进行转移

这样就把状态转移分成了若干个层次

然后发现新的边权如果是0的话会在同一层之间转移

并且可以保证新的边权是没有0环的

那就再做一遍toposort就好了

随便跑一跑多好的

然后同一层你就先跑所有新的权值是0的边,同层转移完了之后你再跑权值不是0的边转移到下一层就可以了


附带Debug全套餐。。。良心对拍程序(懒得删了)


//Author: dream_maker
#include<bits/stdc++.h>
using namespace std;
//----------------------------------------------
//typename
typedef long long ll;
//convenient for
#define fu(a, b, c) for (int a = b; a <= c; ++a)
#define fd(a, b, c) for (int a = b; a >= c; --a)
#define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
//inf of different typename
const int INF_of_int = 1e9;
const ll INF_of_ll = 1e18;
//fast read and write
template <typename T>
void Read(T &x) {
bool w = 1;x = 0;
char c = getchar();
while (!isdigit(c) && c != '-') c = getchar();
if (c == '-') w = 0, c = getchar();
while (isdigit(c)) {
x = (x<<1) + (x<<3) + c -'0';
c = getchar();
}
if (!w) x = -x;
}
template <typename T>
void Write(T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) Write(x / 10);
putchar(x % 10 + '0');
}
//----------------------------------------------
//#define Debug
typedef pair<int, int> pi;
const int N = 2e5 + 10;
const int M = 55;
struct Edge {
int u, v, w, nxt;
bool dir, eras;
} E[N << 1];
int head[N], tot = 0, vis[N];
int dis1[N], dis2[N], deg[N], topoid[N];
int n, m, k, p, minidis, dp[N][M];
void init() {
tot = 0;
fu(i, 1, n) head[i] = deg[i] = 0;
fu(i, 1, n)
fu(j, 0, k) dp[i][j] = 0;
}
void addedge(int u, int v, int w) {
#ifdef Debug
printf("EDGE:[%d, %d]: %d\n", u, v, w);
#endif
E[++tot] = (Edge) {u, v, w, head[u], 0, 0}, head[u] = tot;
E[++tot] = (Edge) {v, u, w, head[v], 1, 0}, head[v] = tot;
}
void add(int &a, int b) {
if ((a += b) >= p) a -= p;
}
void Dijkstra1() {
static priority_queue<pi, vector<pi>, greater<pi> > q;
fu(i, 1, n) dis1[i] = INF_of_int, vis[i] = 0;
dis1[1] = 0;
q.push(pi(dis1[1], 1));
while (q.size()) {
int u = q.top().second; q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].v;
if (!E[i].dir && dis1[v] > dis1[u] + E[i].w) {
dis1[v] = dis1[u] + E[i].w;
q.push(pi(dis1[v], v));
}
}
}
}
void Dijkstra2() {
static priority_queue<pi, vector<pi>, greater<pi> > q;
fu(i, 1, n) dis2[i] = INF_of_int, vis[i] = 0;
dis2[n] = 0;
q.push(pi(dis2[n], n));
while (q.size()) {
int u = q.top().second; q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].v;
if (E[i].dir && dis2[v] > dis2[u] + E[i].w) {
dis2[v] = dis2[u] + E[i].w;
q.push(pi(dis2[v], v));
}
}
}
}
bool toposort() {
int ind = 0;
fu(i, 1, n) vis[i] = 0;
fu(i, 1, tot) {
if (E[i].eras || E[i].w) continue;
deg[E[i].v]++;
}
static queue<int> topo;
fu(i, 1, n) if (!deg[i]) topo.push(i);
while (topo.size()) {
int u = topo.front(); topo.pop();
#ifdef Debug
printf("Topotost :: Reach: %d\n", u);
#endif
topoid[++ind] = u, vis[u] = 1;
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].v;
if (E[i].w || E[i].eras) continue;
if (--deg[v] == 0) topo.push(v);
}
}
fu(i, 1, n) if (deg[i]) return 1;
fu(i, 1, n) if (!vis[i]) topoid[++ind] = i;
return 0;
}
void DP() {
dp[1][0] = 1;
fu(j, 0, k) {
#ifdef Debug
printf("In the case :: %d\n", j);
#endif
fu(id, 1, n) if (dp[topoid[id]][j]) {
int u = topoid[id];
for (int i = head[u]; i; i = E[i].nxt) {
if (E[i].eras || E[i].w) continue;
int v = E[i].v;
add(dp[v][j], dp[u][j]);
#ifdef Debug
printf("Trans between:[%d, %d] :: old value : %d new value : %d\n", u, v, j, E[i].w);
#endif
}
}
fu(id, 1, n) if (dp[topoid[id]][j]) {
int u = topoid[id];
for (int i = head[u]; i; i = E[i].nxt) {
if (E[i].eras || !E[i].w) continue;
int v = E[i].v;
if (j + E[i].w > k) continue;
add(dp[v][j + E[i].w], dp[u][j]);
#ifdef Debug
printf("Trans between:[%d, %d] :: old value : %d new value : %d\n", u, v, j, E[i].w);
#endif
}
}
}
}
void solve() {
Read(n), Read(m), Read(k), Read(p);
init();
fu(i, 1, m) {
int u, v, w;
Read(u), Read(v), Read(w);
addedge(u, v, w);
}
Dijkstra1();
Dijkstra2();
#ifdef Debug
fu(i, 1, n) cout<<dis1[i]<<" "<<dis2[i]<<endl;
#endif
minidis = dis1[n];
if (dis1[n] >= INF_of_int) {
printf("0\n");
return;
}
fu(i, 1, tot) {
if (!E[i].dir) {
if (dis1[E[i].u] + E[i].w + dis2[E[i].v] > minidis + k) E[i].eras = 1;
} else E[i].eras = 1;
}
if (toposort()) {
printf("-1\n");
return;
}
fu(i, 1, tot) {
if (E[i].eras) continue;
E[i].w = dis2[E[i].v] - dis2[E[i].u] + E[i].w;
}
toposort();
#ifdef Debug
fu(i, 1, n) printf("%d ", topoid[i]);
putchar('\n');
#endif
DP();
int ans = 0;
fu(i, 0, k) add(ans, dp[n][i]);
Write(ans), putchar('\n');
}
int main() {
#ifdef dream_maker
freopen("input.txt", "r", stdin);
#endif
int T; Read(T);
while (T--) solve();
return 0;
}

LOJ2316. 「NOIP2017」逛公园【DP】【最短路】【思维】的更多相关文章

  1. 【LOJ2316】「NOIP2017」逛公园

    [题目链接] [点击打开链接] [题目概括] 对给定\(K\),起点\(1\)到终点\(n\)中对长度为\([L,L+K]\)的路径计数. \(L\)为\(1\)到\(n\)的最短路长度. [思路要点 ...

  2. 「NOIP2017」逛公园

    传送门 Luogu 解题思路 考虑 \(\text{DP}\). 设 \(f[u][k]\) 表示从 \(u\) 到 \(n\) 走过不超过 \(Mindis(u, n) + k\) 距离的方案数. ...

  3. 【NOIP2017】逛公园(最短路图,拓扑排序,计数DP)

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

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

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

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

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

  6. [luogu P3953] [noip2017 d1t3] 逛公园

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

  7. 「NOIP2017」宝藏

    「NOIP2017」宝藏 题解 博客阅读效果更佳 又到了一年一度NOIPCSP-S 赛前复习做真题的时间 于是就遇上了这道题 首先观察数据范围 \(1 \le n \le 12\) ,那么极大可能性是 ...

  8. 「WC2013」糖果公园

    「WC2013」糖果公园 传送门 树上带修莫队板子题. 看懂题意后就是板子题了. 参考代码: #include <algorithm> #include <cstdio> #i ...

  9. 逛公园「NOIP2017」最短路+DP

    大家好我叫蒟蒻,这是我的第一篇信竞题解blog [题目描述] 策策同学特别喜欢逛公园. 公园可以看成一张 \(N\) 个点 \(M\) 条边构成的有向图,且没有自环和重边.其中 \(1\) 号点是公园 ...

随机推荐

  1. Check out our list of adidas NMD Singapore retailers

    The adidas NMD Singapore is confirmed to produce on The month of january 14th at select adidas Origi ...

  2. fatal error C1010: unexpected end of file while looking for precompiled header directive

    在编译VS时候,出现fatal error C1010: unexpected end of file while looking for precompiled head. 问题详细解释:致命错误C ...

  3. http之工作原理

    HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端.HTTP协议采用了请求/响应模型.客户端向服务器发送一个请求报文,请求报文包含请求的方法.URL. ...

  4. 466E - Information Graph 巧妙的判断祖先于孩子的关系

    这题说的是给了一个公司员工100000 然后现在又3种操作第一种将y置为x的父亲,第二种操作将文件给第x个人签他签完给他的上司签,一直到没有上司为止,第三种操作问x是否签了第i份文件,然后 我们只要直 ...

  5. 【软件安装】Xshell + XFtp

    [问题]xshell evaluation period has expired 今天发现一个xshell过期的事情,其实官方提供对应的校园版本供大家使用 进入官方下载地址:xshell地址 填写个人 ...

  6. # 20145106 《Java程序设计》第3周学习总结

    教材学习内容总结 在本周的学习中,我看到了这样一句话:"使用java撰写程序几乎都是在使用对象(object),要产生对象必须先定义类(class),类是对象的设计图,对象是类的实例(ins ...

  7. Vim 操作命令不完全汇总

    .命令:"重复上次修改": x命令:删除光标下的字符: u命令:撤销上次修改: dd命令:删除整行: >G命令:增加从当前行到文档末尾处的层级缩进: $命令:把光标移至行尾: ...

  8. HDU 2732 Leapin' Lizards(最大流)

    http://acm.hdu.edu.cn/showproblem.php?pid=2732 题意: 给出n行的网格,还有若干只蜥蜴,每只蜥蜴一开始就在一个格子之中,并且给出蜥蜴每次的最大跳跃长度d. ...

  9. codeforces 256 div2 C. Painting Fence 分治

    C. Painting Fence time limit per test 1 second memory limit per test 512 megabytes input standard in ...

  10. RabbitMQ入门_02_HelloWorld

    A. AMQP基础 RabbitMQ 并不是基于 Java 开发人员熟悉的 JMS 规范设计开发的,而是基于一个比 JMS 更新更合理的 AMQP (Advanced Message Queuing ...