Description给定一张 n n n 个点 m m m 条边的有向图,有边权,进行 q q q 次询问(n , m , q ≤ 2 × 1 0 5 n,m,q\leq 2\times 10^5 n , m , q ≤ 2 × 1 0 5 ,边权为不超过 1 0 9 10^9 1 0 9 的正整数)。
每次询问给定三个参数 v , s , t ( 0 ≤ s < t ≤ 1 0 9 ) v,s,t(0\leq s<t\leq 10^9) v , s , t ( 0 ≤ s < t ≤ 1 0 9 ) ,你需要回答是否存在一条起点终点均为 v v v 的路径,满足 路径长 + s ≡ 0 ( m o d t ) 路径长+s\equiv 0\pmod t 路 径 长 + s ≡ 0 ( m o d t ) 。
Solution先缩强连通分量,容易发现一个点只能走与其属于同一个强连通分量的点。
把强连通分量中的所有环拿出来,然后把这些环都走 t t t 遍,根据欧拉回路,一定存在这样的走法。后面想走任何一个环任意次都是可以的了,因为已经先把每个环走 t t t 次,整个分量已经连通,而加入一个环仍然满足入度出度的限制,所以仍然存在从任意一点开始的回路。
现在设环长为 a 1 , a 2 , … , a k a_1,a_2,\ldots,a_k a 1 , a 2 , … , a k ,根据贝祖定理,所有满足是 gcd ( a 1 , a 2 , … , a k , t ) \gcd(a_1,a_2,\ldots,a_k,t) g cd( a 1 , a 2 , … , a k , t ) 的倍数的数都能被表示,于是现在只需要求出每个环长的 gcd \gcd g cd 了。
由于环的数量可能很多,不能一一找出。但是注意到只需要保留简单环,而一个简单环一定包含至少一条返祖边或者横叉边,考虑对这两种边进行分讨。设边为 ( u → v , w ) (u\to v,w) ( u → v , w ) ,点 u u u 到根的距离是 d i s u dis_u d i s u 。
返祖边:把 d i s u + w − d i s v dis_u+w-dis_v d i s u + w − d i s v 加入贡献。 横叉边:由于 u u u 走到 v v v 之后要再走回来就必须经过一个只有返祖边的简单环,把这个有横叉边的环边权减去只有返祖边的环的值加入贡献,这个贡献还是 d i s u + w − d i s v dis_u+w-dis_v d i s u + w − d i s v 。 tarjan 求出强连通分量并用一遍 dfs 求出每个强连通分量环长的 gcd \gcd g cd 即可。
时间复杂度:O ( n + ( m + q ) log V ) O(n+(m+q)\log V) O ( n + ( m + q ) log V ) 。
Code1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include <bits/stdc++.h> using i64 = long long ;const int kMaxN = 2e5 + 5 ;int n, m, q, cnt_scc;int dfn[kMaxN], low[kMaxN], bel[kMaxN];bool ins[kMaxN]; i64 dis[kMaxN], res[kMaxN]; std::vector<std::pair<int , int >> G[kMaxN];void dfs (int u) { static bool vis[kMaxN]; vis[u] = 1 ; for (auto [v, w] : G[u]) { if (bel[v] != bel[u]) continue ; if (!vis[v]) { dis[v] = dis[u] + w; dfs (v); } else { res[bel[u]] = std::__gcd(res[bel[u]], llabs (dis[u] + w - dis[v])); } } }void tarjan (int u) { static int cnt = 0 ; static std::stack<int > stk; dfn[u] = low[u] = ++cnt, ins[u] = 1 , stk.emplace (u); for (auto [v, w] : G[u]) { if (!dfn[v]) { tarjan (v); low[u] = std::min (low[u], low[v]); } else if (ins[v]) { low[u] = std::min (low[u], dfn[v]); } } if (low[u] == dfn[u]) { ++cnt_scc; for (; !stk.empty ();) { int t = stk.top (); stk.pop (); bel[t] = cnt_scc, ins[t] = 0 ; if (t == u) break ; } dfs (u); } }void dickdreamer () { std::cin >> n >> m; for (int i = 1 ; i <= m; ++i) { int u, v, w; std::cin >> u >> v >> w; G[u].emplace_back (v, w); } for (int i = 1 ; i <= n; ++i) if (!dfn[i]) tarjan (i); std::cin >> q; for (int i = 1 ; i <= q; ++i) { int v, s, t; std::cin >> v >> s >> t; std::cout << (s % std::__gcd((i64)t, res[bel[v]]) == 0 ? "YES\n" : "NO\n" ); } }int32_t main () {#ifdef ORZXKR freopen ("in.txt" , "r" , stdin); freopen ("out.txt" , "w" , stdout);#endif std::ios::sync_with_stdio (0 ), std::cin.tie (0 ), std::cout.tie (0 ); int T = 1 ; while (T--) dickdreamer (); return 0 ; }