QOJ.ac

QOJ

IDProblemSubmitterResultTimeMemoryLanguageFile sizeSubmit timeJudge time
#116507#4885. Triangular Cactus Pathshos_lyricTL 48ms98700kbC++1412.1kb2023-06-29 13:40:142023-06-29 13:40:16

Judging History

你现在查看的是最新测评结果

  • [2023-08-10 23:21:45]
  • System Update: QOJ starts to keep a history of the judgings of all the submissions.
  • [2023-06-29 13:40:16]
  • 评测
  • 测评结果:TL
  • 用时:48ms
  • 内存:98700kb
  • [2023-06-29 13:40:14]
  • 提交

answer

#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

using namespace std;

using Int = long long;

template <class T1, class T2> ostream &operator<<(ostream &os, const pair<T1, T2> &a) { return os << "(" << a.first << ", " << a.second << ")"; };
template <class T> ostream &operator<<(ostream &os, const vector<T> &as) { const int sz = as.size(); os << "["; for (int i = 0; i < sz; ++i) { if (i >= 256) { os << ", ..."; break; } if (i > 0) { os << ", "; } os << as[i]; } return os << "]"; }
template <class T> void pv(T a, T b) { for (T i = a; i != b; ++i) cerr << *i << " "; cerr << endl; }
template <class T> bool chmin(T &t, const T &f) { if (t > f) { t = f; return true; } return false; }
template <class T> bool chmax(T &t, const T &f) { if (t < f) { t = f; return true; } return false; }

////////////////////////////////////////////////////////////////////////////////
template <unsigned M_> struct ModInt {
  static constexpr unsigned M = M_;
  unsigned x;
  constexpr ModInt() : x(0U) {}
  constexpr ModInt(unsigned x_) : x(x_ % M) {}
  constexpr ModInt(unsigned long long x_) : x(x_ % M) {}
  constexpr ModInt(int x_) : x(((x_ %= static_cast<int>(M)) < 0) ? (x_ + static_cast<int>(M)) : x_) {}
  constexpr ModInt(long long x_) : x(((x_ %= static_cast<long long>(M)) < 0) ? (x_ + static_cast<long long>(M)) : x_) {}
  ModInt &operator+=(const ModInt &a) { x = ((x += a.x) >= M) ? (x - M) : x; return *this; }
  ModInt &operator-=(const ModInt &a) { x = ((x -= a.x) >= M) ? (x + M) : x; return *this; }
  ModInt &operator*=(const ModInt &a) { x = (static_cast<unsigned long long>(x) * a.x) % M; return *this; }
  ModInt &operator/=(const ModInt &a) { return (*this *= a.inv()); }
  ModInt pow(long long e) const {
    if (e < 0) return inv().pow(-e);
    ModInt a = *this, b = 1U; for (; e; e >>= 1) { if (e & 1) b *= a; a *= a; } return b;
  }
  ModInt inv() const {
    unsigned a = M, b = x; int y = 0, z = 1;
    for (; b; ) { const unsigned q = a / b; const unsigned c = a - q * b; a = b; b = c; const int w = y - static_cast<int>(q) * z; y = z; z = w; }
    assert(a == 1U); return ModInt(y);
  }
  ModInt operator+() const { return *this; }
  ModInt operator-() const { ModInt a; a.x = x ? (M - x) : 0U; return a; }
  ModInt operator+(const ModInt &a) const { return (ModInt(*this) += a); }
  ModInt operator-(const ModInt &a) const { return (ModInt(*this) -= a); }
  ModInt operator*(const ModInt &a) const { return (ModInt(*this) *= a); }
  ModInt operator/(const ModInt &a) const { return (ModInt(*this) /= a); }
  template <class T> friend ModInt operator+(T a, const ModInt &b) { return (ModInt(a) += b); }
  template <class T> friend ModInt operator-(T a, const ModInt &b) { return (ModInt(a) -= b); }
  template <class T> friend ModInt operator*(T a, const ModInt &b) { return (ModInt(a) *= b); }
  template <class T> friend ModInt operator/(T a, const ModInt &b) { return (ModInt(a) /= b); }
  explicit operator bool() const { return x; }
  bool operator==(const ModInt &a) const { return (x == a.x); }
  bool operator!=(const ModInt &a) const { return (x != a.x); }
  friend std::ostream &operator<<(std::ostream &os, const ModInt &a) { return os << a.x; }
};
////////////////////////////////////////////////////////////////////////////////

constexpr unsigned MO = 998244353;
using Mint = ModInt<MO>;

constexpr int LIM_INV = 400'010;
Mint inv[LIM_INV], fac[LIM_INV], invFac[LIM_INV];

void prepare() {
  inv[1] = 1;
  for (int i = 2; i < LIM_INV; ++i) {
    inv[i] = -((Mint::M / i) * inv[Mint::M % i]);
  }
  fac[0] = invFac[0] = 1;
  for (int i = 1; i < LIM_INV; ++i) {
    fac[i] = fac[i - 1] * i;
    invFac[i] = invFac[i - 1] * inv[i];
  }
}
Mint binom(Int n, Int k) {
  if (n < 0) {
    if (k >= 0) {
      return ((k & 1) ? -1 : +1) * binom(-n + k - 1, k);
    } else if (n - k >= 0) {
      return (((n - k) & 1) ? -1 : +1) * binom(-k - 1, n - k);
    } else {
      return 0;
    }
  } else {
    if (0 <= k && k <= n) {
      assert(n < LIM_INV);
      return fac[n] * invFac[k] * invFac[n - k];
    } else {
      return 0;
    }
  }
}


#ifndef LIBRA_GRAPH_GRAPH_H_
#define LIBRA_GRAPH_GRAPH_H_

#include <assert.h>
#include <ostream>
#include <utility>
#include <vector>

using std::ostream;
using std::pair;
using std::vector;

////////////////////////////////////////////////////////////////////////////////
// neighbors of u: [pt[u], pt[u + 1])
struct Graph {
  int n;
  vector<pair<int, int>> edges;
  vector<int> pt;
  vector<int> zu;

  Graph() : n(0), edges() {}
  explicit Graph(int n_) : n(n_), edges() {}
  void ae(int u, int v) {
    assert(0 <= u); assert(u < n);
    assert(0 <= v); assert(v < n);
    edges.emplace_back(u, v);
  }
  void build(bool directed) {
    const int edgesLen = edges.size();
    pt.assign(n + 1, 0);
    if (directed) {
      for (int i = 0; i < edgesLen; ++i) {
        ++pt[edges[i].first];
      }
      for (int u = 0; u < n; ++u) pt[u + 1] += pt[u];
      zu.resize(edgesLen);
      for (int i = edgesLen; --i >= 0; ) {
        zu[--pt[edges[i].first]] = edges[i].second;
      }
    } else {
      for (int i = 0; i < edgesLen; ++i) {
        ++pt[edges[i].first];
        ++pt[edges[i].second];
      }
      for (int u = 0; u < n; ++u) pt[u + 1] += pt[u];
      zu.resize(2 * edgesLen);
      for (int i = edgesLen; --i >= 0; ) {
        const int u = edges[i].first, v = edges[i].second;
        zu[--pt[u]] = v;
        zu[--pt[v]] = u;
      }
    }
  }

  inline int deg(int u) const {
    return pt[u + 1] - pt[u];
  }
  inline int operator[](int j) const {
    return zu[j];
  }
  friend ostream &operator<<(ostream &os, const Graph &g) {
    os << "Graph(n=" << g.n << ";";
    for (int u = 0; u < g.n; ++u) {
      os << " " << u << ":[";
      for (int j = g.pt[u]; j < g.pt[u + 1]; ++j) {
        if (j != g.pt[u]) os << ",";
        os << g[j];
      }
      os << "]";
    }
    os << ")";
    return os;
  }
};
////////////////////////////////////////////////////////////////////////////////

#endif  // LIBRA_GRAPH_GRAPH_H_


// gg: bipartite graph between {vertex} and {biconnected component}
//   (gg.n - n) biconnected components
//   isolated point: not regarded as biconnected component (==> isolated in gg)
// f: DFS forest
struct Biconnected {
  int n;
  Graph g, f, gg;

  Biconnected() : n(0), stackLen(0), zeit(0) {}
  explicit Biconnected(int n_) : n(n_), g(n_), stackLen(0), zeit(0) {}
  void ae(int u, int v) {
    g.ae(u, v);
  }

  int stackLen;
  vector<int> stack;
  vector<int> par, rs;
  int zeit;
  vector<int> dis, fin, low;
  vector<int> cntPar;
  void dfs(int u) {
    stack[stackLen++] = u;
    dis[u] = low[u] = zeit++;
    for (int j = g.pt[u]; j < g.pt[u + 1]; ++j) {
      const int v = g[j];
      if (par[u] == v && !cntPar[u]++) continue;
      if (~dis[v]) {
        if (low[u] > dis[v]) low[u] = dis[v];
      } else {
        f.ae(u, v);
        par[v] = u;
        rs[v] = rs[u];
        dfs(v);
        if (low[u] > low[v]) low[u] = low[v];
        if (dis[u] <= low[v]) {
          const int x = gg.n++;
          for (; ; ) {
            const int w = stack[--stackLen];
            gg.ae(w, x);
            if (w == v) break;
          }
          gg.ae(u, x);
        }
      }
    }
    fin[u] = zeit;
  }
  void build() {
    g.build(false);
    f = Graph(n);
    gg = Graph(n);
    stack.resize(n);
    par.assign(n, -1);
    rs.assign(n, -1);
    zeit = 0;
    dis.assign(n, -1);
    fin.assign(n, -1);
    low.assign(n, -1);
    cntPar.assign(n, 0);
    for (int u = 0; u < n; ++u) if (!~dis[u]) {
      stackLen = 0;
      rs[u] = u;
      dfs(u);
    }
    f.build(true);
    gg.build(false);
  }

  // Returns true iff u is an articulation point
  //   <=> # of connected components increases when u is removed.
  inline bool isArt(int u) const {
    return (gg.deg(u) >= 2);
  }

  // Returns w s.t. w is a child of u and a descendant of v in the DFS forest.
  // Returns -1 instead if v is not a proper descendant of u
  //   O(log(deg(u))) time
  int dive(int u, int v) const {
    if (dis[u] < dis[v] && dis[v] < fin[u]) {
      int j0 = f.pt[u], j1 = f.pt[u + 1];
      for (; j0 + 1 < j1; ) {
        const int j = (j0 + j1) / 2;
        ((dis[f[j]] <= dis[v]) ? j0 : j1) = j;
      }
      return f[j0];
    } else {
      return -1;
    }
  }
  // Returns true iff there exists a v-w path when u is removed.
  //   O(log(deg(u))) time
  bool isStillReachable(int u, int v, int w) const {
    assert(0 <= u); assert(u < n);
    assert(0 <= v); assert(v < n);
    assert(0 <= w); assert(w < n);
    assert(u != v);
    assert(u != w);
    if (rs[v] != rs[w]) return false;
    if (rs[u] != rs[v]) return true;
    const int vv = dive(u, v);
    const int ww = dive(u, w);
    if (~vv) {
      if (~ww) {
        return (vv == ww || (dis[u] > low[vv] && dis[u] > low[ww]));
      } else {
        return (dis[u] > low[vv]);
      }
    } else {
      if (~ww) {
        return (dis[u] > low[ww]);
      } else {
        return true;
      }
    }
  }
};

////////////////////////////////////////////////////////////////////////////////


int N, M;
vector<int> A, B;

Biconnected C;

constexpr int E = 19;
constexpr int MAX_N = 400'010;
int dep[MAX_N];
int ppar[E][MAX_N];
int sum2[E][MAX_N];
int sum3[E][MAX_N];

void dfs(int u, int p) {
  dep[u] = (~p) ? (dep[p] + 1) : 0;
  ppar[0][u] = p;
  for (int j = C.gg.pt[u]; j < C.gg.pt[u + 1]; ++j) {
    const int v = C.gg[j];
    if (p != v) {
      dfs(v, u);
    }
  }
}

int lca(int u, int v) {
  for (int e = E; --e >= 0; ) {
    if (dep[u] - (1 << e) >= dep[v]) {
      u = ppar[e][u];
    }
    if (dep[v] - (1 << e) >= dep[u]) {
      v = ppar[e][v];
    }
  }
  for (int e = E; --e >= 0; ) {
    if (ppar[e][u] != ppar[e][v]) {
      u = ppar[e][u];
      v = ppar[e][v];
    }
  }
  if (u != v) {
    u = ppar[0][u];
    v = ppar[0][v];
  }
  return u;
}

int main() {
  prepare();
  
  for (; ~scanf("%d%d", &N, &M); ) {
    A.resize(M);
    B.resize(M);
    for (int i = 0; i < M; ++i) {
      scanf("%d%d", &A[i], &B[i]);
      --A[i];
      --B[i];
    }
    
    C = Biconnected(N);
    for (int i = 0; i < M; ++i) {
      C.ae(A[i], B[i]);
    }
    C.build();
cerr<<"gg = "<<C.gg<<endl;
    
    fill(dep, dep + C.gg.n, -1);
    for (int u = 0; u < C.gg.n; ++u) if (!~dep[u]) {
      dfs(u, -1);
    }
    fill(sum2[0], sum2[0] + C.gg.n, 0);
    for (int u = N; u < C.gg.n; ++u) {
      ((C.gg.deg(u) == 2) ? sum2 : sum3)[0][u] = 1;
    }
    for (int e = 0; e < E - 1; ++e) {
      for (int u = 0; u < C.gg.n; ++u) {
        const int p = ppar[e][u];
        if (~p) {
          ppar[e + 1][u] = ppar[e][p];
          sum2[e + 1][u] = sum2[e][u] + sum2[e][p];
          sum3[e + 1][u] = sum3[e][u] + sum3[e][p];
        } else {
          ppar[e + 1][u] = -1;
          sum2[e + 1][u] = sum2[e][u];
          sum3[e + 1][u] = sum3[e][u];
        }
      }
    }
    
    int Q;
    scanf("%d", &Q);
    for (; Q--; ) {
      int S, T, K;
      scanf("%d%d%d", &S, &T, &K);
      --S;
      --T;
      
      int s2 = 0, s3 = 0;
      auto add = [&](int u, int d) -> void {
        for (int e = E; --e >= 0; ) if (d >> e & 1) {
          s2 += sum2[e][u];
          s3 += sum3[e][u];
          u = ppar[e][u];
        }
      };
      const int l = lca(S, T);
      add(S, dep[S] - dep[l]);
      add(T, dep[T] - dep[l]);
      if (l >= N) {
        ++((C.gg.deg(l) == 2) ? s2 : s3);
      }
// cerr<<"s2 = "<<s2<<", s3 = "<<s3<<endl;
      Mint ans = 0;
      if (s2 + s3 <= K && K <= s2 + 2 * s3) {
        ans = binom(s3, K - (s2 + s3));
      }
      printf("%u\n", ans.x);
    }
  }
  return 0;
}

Details

Tip: Click on the bar to expand more detailed information

Test #1:

score: 100
Accepted
time: 6ms
memory: 98700kb

input:

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

output:

1
0
1
2
1
0

result:

ok 6 numbers

Test #2:

score: 0
Accepted
time: 3ms
memory: 97128kb

input:

2 1
1 2
8
1 1 0
1 1 1
1 2 0
1 2 1
2 1 0
2 1 1
2 2 0
2 2 1

output:

1
0
0
1
0
1
1
0

result:

ok 8 numbers

Test #3:

score: 0
Accepted
time: 48ms
memory: 98224kb

input:

50 70
41 24
9 15
29 19
21 11
1 14
5 27
34 48
10 32
34 49
46 3
22 33
34 39
16 30
22 45
7 16
25 30
43 17
22 44
5 25
41 49
29 32
39 25
10 4
45 27
13 38
29 7
3 35
14 30
50 2
8 11
13 35
18 26
34 40
38 36
7 19
12 3
25 26
30 42
21 8
12 46
44 33
14 31
47 2
25 46
20 19
49 24
15 43
18 25
13 36
27 22
4 32
30 3...

output:

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
3
0
0
0
0
0
4
0
0
15
5
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
10
2
0
0
0
0
0
6
0
0
0
0
0
0
0
0
7
0
0
0
0
3
0
6
0
0
0
0
7
0
6
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
15
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0...

result:

ok 200000 numbers

Test #4:

score: 0
Accepted
time: 8ms
memory: 97720kb

input:

99 147
26 66
27 92
75 90
91 64
69 15
70 61
52 59
3 86
41 99
47 71
26 43
99 83
43 80
42 69
4 75
66 71
68 38
31 57
5 91
74 2
90 4
32 11
1 31
77 5
76 8
12 60
79 42
48 89
22 14
36 76
45 91
43 65
99 86
69 16
85 11
19 28
15 52
49 85
84 68
28 85
93 68
16 15
42 9
71 51
72 92
84 2
13 50
9 44
97 78
11 60
98 3...

output:

0
0
0
231867712
1176
13983816
0
838535532
57966928
0
259558945
995677382
49
58518925
231867712
0
0
0
0
699660129
0
18424
0
0
0
0
425254360
596115770
0
0
0
0
435689908
0
0
578343994
351113550
0
0
1
186830027
578343994
211876
0
596115770
0
186830027
0
58518925
0
1906884
304945365
0
304945365
0
1
0
0
2...

result:

ok 99 numbers

Test #5:

score: -100
Time Limit Exceeded

input:

199999 299997
46838 7499
94674 132821
113340 39783
26895 102985
48012 197970
142680 5947
103806 184307
136126 111090
130225 128927
158116 75097
24205 199579
53104 138584
43659 53794
139767 62161
93131 22718
129300 168198
49873 179193
194655 168999
113749 118408
37725 48137
151840 84343
111148 8100
1...

output:


result: