Luogu P3953NOIP2017逛公园最短路+拓扑排序+动态规划

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P3953NOIP2017逛公园最短路+拓扑排序+动态规划相关的知识,希望对你有一定的参考价值。

题目描述

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

策策每天都会去逛公园,他总是从1号点进去,从NN号点出来。

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

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

为避免输出过大,答案对PP取模。

如果有无穷多条合法的路线,请输出−1。

输入输出格式

输入格式:

第一行包含一个整数 T, 代表数据组数。

接下来TT组数据,对于每组数据: 第一行包含四个整数 N,M,K,PN,M,K,P,每两个整数之间用一个空格隔开。

接下来MM行,每行三个整数a_i,b_i,c_iai?,bi?,ci?,代表编号为a_i,b_iai?,bi?的点之间有一条权值为 c_ici?的有向边,每两个整数之间用一个空格隔开。

输出格式:

输出文件包含T 行,每行一个整数代表答案。

输入输出样例

输入样例#1:

2
5 7 2 10
1 2 1
2 4 0
4 5 2
2 3 2
3 4 1
3 5 2
1 5 3
2 2 0 10
1 2 0
2 1 0

输出样例#1:

3
-1

说明

【样例解释1】

对于第一组数据,最短路为 3。 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3 条合法路径。

【测试数据与约定】

对于不同的测试点,我们约定各种参数的规模不会超过如下

测试点编号  TT   NN   MM   KK   是否有0边
1 5 5 10 0
2 5 1000 2000 0
3 5 1000 2000 50
4 5 1000 2000 50
5 5 1000 2000 50
6 5 1000 2000 50
7 5 100000 200000 0
8 3 100000 200000 50
9 3 100000 200000 50
10 3 100000 200000 50

对于 100%的数据, 1 <= P <= 10^9,1 <= a_i,b_i <= N ,0 <= c_i <= 1000, 1P109,1ai?,bi?0ci?1000。

数据保证:至少存在一条合法的路线。

 

Solution : 

首先从起点和终点的最短路一定是要跑的。

注意到k不大于50, nk的动态规划是可以考虑的。

设起点到第i个点最短路为dis[i] , 这样dp[i][j]表示到第i个点长度为dis[i]+j的路径条数。

考虑到可以同一层转移,显然这种转移边满足dis[x] + len == dis[y]。

那么我么拓扑排序一下确定转移顺序即可。

考虑-1的情况,若有零环且存在通过零环并且长度<=dis[n]+k的路径则答案无限大。

namespace可好写了,写得可长了。

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define travel(x, i) for (int i = fir[x]; i; i = e[i].nxt)
using namespace std;

namespace fastIO {
#define buf_size 1000000
  bool error;
  inline char gc() {
    static char buf[buf_size + 1], *l = buf, *r = buf;
    if (l == r) {
      l = buf;
      r = buf + fread(buf, 1, buf_size, stdin);
      if (l == r) {error = 1; return -1;}
    }
    return *l ++;
  }
  inline bool isnum(char ch) {
    return (‘0‘ <= ch && ch <= ‘9‘) || ch == ‘-‘;
  }
  inline void read(int &x) {
    char ch; x = 0;
    while (!isnum(ch = gc()) && !error);
    if (error) return;
    do {x = (x << 1) + (x << 3) + ch - ‘0‘;} while (isdigit(ch = gc()) && !error);
  }
#undef buf_size
}
using namespace fastIO;


typedef pair <int, int> pii;
const int N = 1e5 + 5;
const int M = 2e5 + 5;

int n, m, k, mod;
int ans[N][52];

namespace Graph1 {
  struct edge {
    int nxt, to, len;
  } e[M];
  int fir[N], dis[N], cnt;
  bool in[N];

  inline void clear() {memset(fir, 0, sizeof fir); cnt = 0;}
  inline void add(int x, int y, int l) {
    e[++ cnt] = (edge){fir[x], y, l};
    fir[x] = cnt;
  }
  inline void dijkstra() {
    memset(dis, -1, sizeof dis);
    memset(in, 0, sizeof in);
    priority_queue <pii, vector <pii>, greater <pii> > Q;
    Q.push(make_pair(0, 1));
    dis[1] = 0;
    pii cur;
    int x;
    while (!Q.empty()) {
      cur = Q.top(); Q.pop();
      x = cur.second;
      if (in[x]) continue;
      in[x] = 1;
      travel(x, i)
        if (!in[e[i].to] && (dis[x] + e[i].len < dis[e[i].to] || dis[e[i].to] == -1)) {
          dis[e[i].to] = dis[x] + e[i].len;
          Q.push(make_pair(dis[e[i].to], e[i].to));
        }
    }
  }
}

namespace Graph2 {
  struct edge {
    int nxt, to, len;
  } e[M];
  int fir[N], dis[N], cnt;
  bool in[N];
        
  inline void clear() {memset(fir, 0, sizeof fir); cnt = 0;}        
  inline void add(int x, int y, int l) {
    e[++ cnt] = (edge){fir[x], y, l};
    fir[x] = cnt;
  }
  inline void dijkstra() {
    memset(dis, -1, sizeof dis);
    memset(in, 0, sizeof in);
    priority_queue <pii, vector <pii>, greater <pii> > Q;
    Q.push(make_pair(0, n));
    dis[n] = 0;
    pii cur;
    int x;
    while (!Q.empty()) {
      cur = Q.top(); Q.pop();
      x = cur.second;
      if (in[x]) continue;
      in[x] = 1;
      travel(x, i)
        if (!in[e[i].to] && (dis[x] + e[i].len < dis[e[i].to] || dis[e[i].to] == -1)) {
          dis[e[i].to] = dis[x] + e[i].len;
          Q.push(make_pair(dis[e[i].to], e[i].to));
        }
    }
  }
}

namespace Graph3 {
  struct edge {
    int nxt, to;
  } e[M];
  int fir[N], cnt = 0, tot;
  int ord[N], deg[N];
    
  inline void clear() {
    memset(fir, 0, sizeof fir);
    memset(deg, 0, sizeof deg);
    cnt = 0;
  }
  inline void add(int x, int y) {
    e[++ cnt] = (edge){fir[x], y};
    fir[x] = cnt;
    deg[y] ++;
  }
  inline void topo() {
    queue <int> Q;
    for (int i = 1; i <= n; i ++)
      if (!deg[i]) Q.push(i);
    int x; tot = 0;
    while (!Q.empty()) {
      x = Q.front(); Q.pop();
      ord[++ tot] = x;
      travel(x, i) {
        deg[e[i].to] --;
        if (!deg[e[i].to]) Q.push(e[i].to);
      }
    }
  }
}

namespace Graph4 {
  struct edge {
    int nxt, to;
  } e[M];
  int fir[N], deg[N], cnt;
        
  inline void clear() {
    memset(fir, 0, sizeof fir);
    memset(deg, 0, sizeof deg);
    cnt = 0;
  }
  inline void add(int x, int y) {
    e[++ cnt] = (edge){fir[x], y};
    fir[x] = cnt;
    deg[y] ++;
  }
  inline bool topo() {
    queue <int> Q;
    for (int i = 1; i <= n; i ++)
      if (!deg[i]) Q.push(i);
    int x;
    while (!Q.empty()) {
      x = Q.front(); Q.pop();
      travel(x, i) {
        deg[e[i].to] --;
        if (!deg[e[i].to]) Q.push(e[i].to);
      }
    }
    for (int i = 1; i <= n; i ++)
      if (deg[i] && Graph1 :: dis[i] + Graph2 :: dis[i] <= Graph1 :: dis[n] + k) return 0;
    return 1;
  }
}

int x[M], y[M], l[M];

inline void modadd(int &x, int y) {
  if ((x += y) >= mod) x -= mod;
}

using namespace Graph1;

inline void solve(int T) {
  clear();
  Graph2 :: clear();
  Graph3 :: clear();
  Graph4 :: clear();
  read(n); read(m); read(k); read(mod);
  for (int i = 1; i <= m; i ++) {
    read(x[i]); read(y[i]); read(l[i]);
    add(x[i], y[i], l[i]);
    Graph2 :: add(y[i], x[i], l[i]);
  }
  dijkstra();
  Graph2 :: dijkstra();
  for (int i = 1; i <= m; i ++) {
    if (dis[x[i]] == -1 || Graph2 :: dis[y[i]] == -1) continue;
    if (dis[x[i]] + l[i] == dis[y[i]]) Graph3 :: add(x[i], y[i]);
    if (l[i] == 0) Graph4 :: add(x[i], y[i]);
  }
  if (Graph4 :: topo() == 0) return (void)puts("-1");
  Graph3 :: topo();
  memset(ans, 0, sizeof ans);
  ans[1][0] = 1;
  int w, xx, g = Graph3 :: tot;
  for (int i = 0; i <= k; i ++) {
    for (int j = 1; j <= g; j ++) {
      xx = Graph3 :: ord[j];
      if (dis[xx] == -1) continue;
      travel(xx, p) {
        w = dis[xx] + i + e[p].len - dis[e[p].to];
        if (0 <= w && w <= k) modadd(ans[e[p].to][w], ans[xx][i]);
      }
    }
  }
  int Ans = 0;
  for (int i = 0; i <= k; i ++) modadd(Ans, ans[n][i]);
  printf("%d\n", Ans);
}

int main() {
  int T;
  read(T);
  while (T --) solve(T);
  return 0;
}

 

  

 

以上是关于Luogu P3953NOIP2017逛公园最短路+拓扑排序+动态规划的主要内容,如果未能解决你的问题,请参考以下文章

[luogu P3953] [noip2017 d1t3] 逛公园

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

[Luogu P3953] 逛公园 (最短路+拓扑排序+DP)

P3953 NOIP2017 d1t3 逛公园

luogu 3953 逛公园

[Noip2017]逛公园