那么有关的部分就是包含点 v 的强连通分量。由于对于分量上的任意点,存在一个包含其与 v 的环,那么只需要跑 t 遍该环,就可以在某一时刻“出现”在这个点上。又由于复杂环可以由简单环构成,那么问题转化为在强连通分量上选若干个简单环,始环满足长度和模 t 与 -s 同余。可以列出线性同余方程,由裴蜀定理,可知 \gcd(简单环长度集合S \cup t) 整除 t-s。
需要求出强连通分量内环长的最大公因数 G_0,猜想是要把所有环都套进一个生成树上统一去维护。要把生成树补成一个强连通图,那么考虑一个加入非树边 (u,v) 的顺序:v 始终能到达根节点。根据强连通性,显然这是可以构造出的。当加入一条边时,就加入 根节点到 u 的路径 + v 所有到根节点的路径 构成的环。由辗转相减术,实际上相当于加入 dep_u +w-dep_v 。这与具体加号右边的东西是无关的。最后我们得到这些数的最大公因数 G,含义是包含根节点的环长度的 \gcd。据定义得到 G_0 整除 G。
但是容易发现:图上任意的环长度都可以由加入的元素的和构成。有:
推完以后觉得真是精妙绝伦,天作之合。后来在题解区看见了一句概括:所有的环都至少与另一个环进行了求差后求 $\gcd$,等于原来的 $\gcd$。得到了完全的理解。
```cpp
#include <bits/stdc++.h>
using namespace std;
int gcd(int x,int y){return y == 0 ? x : gcd(y, x%y);}
int n, m, q;
vector<int> vec[200002], val[200002];
int dfntot, dfn[200002], low[200002];
int sttop, st[200002];
bool inst[200002];
int belong[200002];
void tarjan(int pos){
dfn[pos] = ++dfntot;
low[pos] = dfn[pos];
st[++sttop] = pos, inst[pos] = 1;
for(int i=0;i<vec[pos].size();i++){
int v = vec[pos][i];
if(dfn[v] != 0){
if(inst[v]) low[pos] = min(low[pos], dfn[v]);
}
else {
tarjan(v);
low[pos] = min(low[pos], low[v]);
}
}
if(low[pos] == dfn[pos]){
while(inst[pos]){
belong[st[sttop]] = pos;
inst[st[sttop]] = 0;
sttop--;
}
}
}
bool flag[200002];
long long f[200002];
long long G[200002];
void dfs(int pos){
for(int i=0;i<vec[pos].size();i++){
int v = vec[pos][i];
if(belong[pos] != belong[v]) continue;
if(!flag[v]){
flag[v] = 1;
f[v] = f[pos] + val[pos][i];
dfs(v);
}
else {
G[belong[pos]] = gcd(G[belong[pos]], abs(f[pos] + val[pos][i] - f[v]));
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u, v, w;
scanf("%d%d%d",&u,&v,&w);
vec[u].push_back(v), val[u].push_back(w);
}
for(int i=1;i<=n;i++) if(dfn[i] == 0) tarjan(i);
for(int i=1;i<=n;i++){
if(!flag[i]) flag[i] = 1, dfs(i);
}
scanf("%d",&q);
while(q--){
int V, S, T;
scanf("%d%d%d",&V,&S,&T);
if((T-S) % gcd(G[belong[V]], T) == 0) printf("YES\n");
else printf("NO\n");
}
return 0;
}
```