LOJ2316. 「NOIP2017」逛公园【DP】【最短路】【思维】
思路
因为我想到的根本不是网上的普遍做法
所以常数出奇的大,而且做法极其暴力
可以形容是带优化的大模拟
进入正题:
首先一个很显然的思路是如果在合法的路径网络里面存在零环是有无数组解的
然后这个直接对所有边权是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】【最短路】【思维】的更多相关文章
- 【LOJ2316】「NOIP2017」逛公园
[题目链接] [点击打开链接] [题目概括] 对给定\(K\),起点\(1\)到终点\(n\)中对长度为\([L,L+K]\)的路径计数. \(L\)为\(1\)到\(n\)的最短路长度. [思路要点 ...
- 「NOIP2017」逛公园
传送门 Luogu 解题思路 考虑 \(\text{DP}\). 设 \(f[u][k]\) 表示从 \(u\) 到 \(n\) 走过不超过 \(Mindis(u, n) + k\) 距离的方案数. ...
- 【NOIP2017】逛公园(最短路图,拓扑排序,计数DP)
题意: 策策同学特别喜欢逛公园. 公园可以看成一张 N 个点 M 条边构成的有向图,且没有自环和重边.其中 1 号点是公园的入口, N 号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花 ...
- P3953 逛公园(dp,最短路)
P3953 逛公园 题目描述 策策同学特别喜欢逛公园.公园可以看成一张NN个点MM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,NN号点是公园的出口,每条边有一个非负权值, 代表策策经 ...
- 【题解】NOIP2017逛公园(DP)
[题解]NOIP2017逛公园(DP) 第一次交挂了27分...我是不是必将惨败了... 考虑这样一种做法,设\(d_i\)表示从该节点到n节点的最短路径,\(dp(i,k)\)表示从\(i\)节点 ...
- [luogu P3953] [noip2017 d1t3] 逛公园
[luogu P3953] [noip2017 d1t3] 逛公园 题目描述 策策同学特别喜欢逛公园.公园可以看成一张$N$个点$M$条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,$N ...
- 「NOIP2017」宝藏
「NOIP2017」宝藏 题解 博客阅读效果更佳 又到了一年一度NOIPCSP-S 赛前复习做真题的时间 于是就遇上了这道题 首先观察数据范围 \(1 \le n \le 12\) ,那么极大可能性是 ...
- 「WC2013」糖果公园
「WC2013」糖果公园 传送门 树上带修莫队板子题. 看懂题意后就是板子题了. 参考代码: #include <algorithm> #include <cstdio> #i ...
- 逛公园「NOIP2017」最短路+DP
大家好我叫蒟蒻,这是我的第一篇信竞题解blog [题目描述] 策策同学特别喜欢逛公园. 公园可以看成一张 \(N\) 个点 \(M\) 条边构成的有向图,且没有自环和重边.其中 \(1\) 号点是公园 ...
随机推荐
- 全自动照片美化软件Photolemur mac特别版
今天小编为大家带来的是世界上首个支持全自动照片美化的软件Photolemur mac特别版.Photolemur中文特别版采用了全新的人工智能AI系统,它能够全自动智能的对您的照片进行曝光.亮度.降噪 ...
- make clean 与 make distclean 的区别
make clean仅仅是清除之前编译的可执行文件及配置文件. 而make distclean要清除所有生成的文件. Makefile 在符合GNU Makefiel惯例的Makefile中,包含了一 ...
- HTML&CSS&Javascript脑图
今天看了极客学院的CSS3部分,加上前几天看过的HTML5部分,现在对HTML和CSS的基础有了系统的认识,正好发现这张图,简直Perfect! 感谢脑图的制作人,虽然不知道是谁,但能把HTML.CS ...
- Broken pipe错误原因
这个异常是由于以下几个原因造成. 1.客户端再发起请求后没有等服务器端相应完,点击了stop按钮,导致服务器端接收到取消请求. 通常情况下是不会有这么无聊的用户,出现这种情况可能是由于用户提交了 ...
- 微信小程序:工具配置 project.config.json
微信小程序:工具配置 project.config.json 一.项目配置文件project.config.json 小程序开发者工具在每个项目的根目录都会生成一个 project.config.js ...
- luogu P1025 数的划分
https://www.luogu.org/problem/show?pid=1025 n的k划分 且不出现划分成0的情况 可以 分为两种情况 所有划分的数 都大于1的情况 至少划分的数里面有1的情 ...
- 癌症免疫细胞治疗知识:CAR-T与TCR-T的区别在哪里?--转载
肿瘤免疫治疗,实际上分为两大类.一种把肿瘤的特征“告诉”免疫细胞,让它们去定位,并造成杀伤:另一种是解除肿瘤对免疫的耐受/屏蔽作用,让免疫细胞重新认识肿瘤细胞,对肿瘤产生攻击(一般来说,肿瘤细胞会巧妙 ...
- C++ 中的关于输出的设置于
▲setw(n)用法: 通俗地讲就是预设宽度 如 cout<<setw(5)<<255<<endl; 结果是: (空格)(空格)255 ▲setfill(char ...
- c++ 容器排序
#include <algorithm> #include <functional> #include <array> #include <iostream& ...
- SSM(Spring+SpringMVC+Mybatis)+Mysql 框架整合搭建流程以及其间注意事项
复习SSM框架,太久没用自己手动撘一个,发现自己还是有很多地方忘记了和没注意的事项... 首先,直接给出总流程: 零.引jar包 1.引包(或者写maven.pom) 一.数据库部分 设计数据库各表结 ...