[LibreOJ 2316][NOIP2017]逛公园

danihao123 posted @ 2018年9月02日 21:08 in 题解 with tags loj NOIP dijkstra 动态规划 , 1271 阅读
转载请注明出处:http://danihao123.is-programmer.com/

给你一张\(n\)个点\(m\)条边的有向图,设从\(1\)到\(n\)的最短路为\(d\),那么请问有多少\(1\)到\(n\)的路径(可以是非简单路径)满足其长度在\([d, d + K]\)中,如果答案为无限大的话输出\(-1\)。

\(1\leq n\leq 10^5, 1\leq m\leq 2\times 10^5, 0\leq K\leq 50\)。

UOJ疯狂卡常……搞不过去力,所以就在loj上搞了一下。

先考虑定义状态\(f_{i, j}\)表示从\(1\)到\(i\),比两点间最短路大恰好\(j\)的边的数量,转移很显然。不过很不幸的事情是,这个DP在是有后消性的(当然如果没有零边的话没有),所以不能直接DP……然后用SPFA转移的会被卡飞……(虽说本来SPFA复杂度就不对啊

那么我们考虑改造一下图,使得DP没后消性……我们考虑用\(p_i\)表示\(1\)到\(i\)的最短路,用\(s_i\)表示\(i\)到\(n\)的最短路,那么对于一条原图边\((u, v, w)\),若有\(p_u + w + s_v\leq p_n + K\)的话那么留下这条边,反之则删掉。那么很显然的一件事是,图中的所有边都可能出现在一个从\(1\)到\(n\)的合法路径中,这样一来解数无穷就等价于这个新图有零环了。

然后这个DP是否就没有后消性了呢?观察到在这个图上跑DP有后消性当且仅当存在零环(因为第二维不可能变小,只可能一直不变绕回来,这样肯定就是有零环),所以只要解数有限的话那么这个图上DP就没有后消性了。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <functional>
#include <utility>
#include <vector>
#include <queue>
using ll = long long;
const int maxn = 100005, maxm = 200005;
using edge = std::pair<int, int>;
struct Graph {
  std::vector<edge> *G;
  Graph() {
    G = new std::vector<edge>[maxn];
  }
  ~Graph() { delete []G; }
  void clear() {
    for(int i = 0; i < maxn; i ++) {
      G[i].clear();
    }
  }
  const std::vector<edge>& operator [](const int p) {
    return G[p];
  }
  void add_edge(int u, int v, int d) {
    G[u].push_back({v, d});
  }
};

int n, m;
using pii = std::pair<ll, int>;
inline void dij(Graph &G, int s, ll *d) {
  static bool vis[maxn]; std::fill(vis, vis + n + 2, false);
  memset(d, 0x2f, sizeof(ll) * (n + 2));
  std::priority_queue<pii, std::vector<pii>, std::greater<pii> > Q;
  d[s] = 0; Q.push({0LL, s});
  while(!Q.empty()) {
    int u = Q.top().second; Q.pop();
    if(vis[u]) continue;
    vis[u] = true;
    for(auto pr : G[u]) {
      int v = pr.first, dist = pr.second;
      if(d[u] + (ll)dist < d[v]) {
        d[v] = d[u] + (ll)dist;
        Q.push({d[v], v});
      }
    }
  }
#ifdef LOCAL
  puts("Dij ended!");
  for(int i = 1; i <= n; i ++) {
    printf("%lld ", d[i]);
  }
  puts("");
#endif
}

Graph PG, RPG, G, RG;
ll k; ll ha;
ll pd[maxn], rpd[maxn];

bool vis[maxn], ins[maxn];
int d[maxn][51];
inline bool kahn() {
  static int inv[maxn]; std::fill(inv, inv + n + 2, 0);
  for(int i = 1; i <= n; i ++) {
    for(auto e : G[i]) {
      int v = e.first;
      if(e.second == 0) {
        inv[v] ++;
      }
    }
  }
  std::queue<int> Q;
  for(int i = 1; i <= n; i ++) {
    if(inv[i] == 0) Q.push(i);
  }
  while(!Q.empty()) {
    int u = Q.front(); Q.pop();
#ifdef LOCAL
    printf("BFS %d\n", u);
#endif
    // V.push_back(u);
    for(auto e : G[u]) {
      int v = e.first;
      if(e.second == 0) {
        inv[v] --;
        if(inv[v] == 0) Q.push(v);
      }
    }
  }
  return (*std::max_element(inv + 1, inv + 1 + n)) == 0;
}
using Node = std::pair<int, int>;
int inv[maxn][51];
void dfs(int i, int j) {
  for(auto e : G[i]) {
    int v = e.first, w = e.second;
    ll nj = -pd[v] + pd[i] + (ll)j + (ll)w;
    if(nj <= k) {
      if(!inv[v][nj]) dfs(v, nj);
      inv[v][nj] ++;
    }
  }
}
inline ll solve() {
  if(!kahn()) return -1;
  for(int i = 1; i <= n; i ++) {
    for(int j = 0; j <= k; j ++) {
      inv[i][j] = 0;
    }
  }
  dfs(1, 0);
  std::queue<Node> Q; Q.push({1, 0});
  memset(d, 0, sizeof(d)); d[1][0] = 1;
  while(!Q.empty()) {
    int u = Q.front().first, j = Q.front().second; Q.pop();
#ifdef LOCAL
    printf("BFS (%d, %d)\n", u, j);
#endif
    for(auto e : G[u]) {
      int v = e.first, w = e.second;
      ll nj = -pd[v] + pd[u] + (ll)j + (ll)w;
      if(nj <= k) {
        d[v][nj] = (d[v][nj] + d[u][j]) % ha;
        inv[v][nj] --;
        if(inv[v][nj] == 0LL) Q.push({v, (int)nj});
      }
    }
  }
  ll ans = 0;
  for(int i = 0; i <= k; i ++) {
    ans += d[n][i];
    if(ans >= ha) ans -= ha;
  }
  return ans;
}

inline void readint(ll &x) {
  x = 0; char c = getchar();
  while(!isdigit(c)) c = getchar();
  while(isdigit(c)) {
    x = x * 10 + (c - '0');
    c = getchar();
  }
}
inline void readint(int &x) {
  x = 0; char c = getchar();
  while(!isdigit(c)) c = getchar();
  while(isdigit(c)) {
    x = x * 10 + (c - '0');
    c = getchar();
  }
}

int main() {
  int T; scanf("%d", &T);
  while(T --) {
    readint(n); readint(m); readint(k); readint(ha);
    PG.clear(); RPG.clear();
    for(int i = 1; i <= m; i ++) {
      int u, v, w;
      readint(u); readint(v); readint(w);
      PG.add_edge(u, v, w); RPG.add_edge(v, u, w);
    }
    dij(PG, 1, pd); dij(RPG, n, rpd);
    G.clear(); // RG.clear();
    for(int i = 1; i <= n; i ++) {
      for(auto e : PG[i]) {
        int v = e.first, w = e.second;
        if((pd[i] + (ll)w + rpd[v]) <= pd[n] + k) {
          G.add_edge(i, v, w); // RG.add_edge(v, u, w);
        }
      }
    }
    printf("%lld\n", solve()); fflush(stdout);
  }
  return 0;
}

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter