[图论] [数论] CF1515G Phoenix and Odometers
posted on 2025-07-07 07:01:20 | under | source
题意:给你一张 \(n\) 点 \(m\) 边的有向图,无自环,边带权。回答 \(q\) 次询问,每次给出 \(v,s,t\),判断是否存在一条起终点为 \(v\) 的回路其边权和 \(w\) 满足 \(w+s\equiv 0\pmod t\)。\(n,m,q\le 2\times 10^5\)。
首先可以对每个强连通分量分别考虑,这样图为强连通图,任意两点存在回路。
根据经验,猜测 \(w\) 即为所有环的一组线性组合。证明容易,对每个环多走 \(t\) 遍对 \(w\) 无影响,一直加到系数为正。那么根据欧拉回路的结论必然存在一种方案。
接下来求出一组基。容易想到只需考虑简单环,也就是只经过一条非树边的环。不过这是无向图还得考虑横叉边。一种构造是定 \(r\) 为根,记 \(d_i\) 为 \(r\to i\) 权值和,\(p_i\) 为 \(i\to r\) 权值和。对于所有边将 \(d_u+w_{u,v}+p_v\) 加入,对所有点将 \(d_i+p_i\) 加入。任意环可表示为所有边减去所有点。
还有更简单的构造,直接把 \(d_u+w_{u,v}-d_v\) 加进来。唯一的问题是这个东西本身代表什么?不难发现是两个环相减。
记 \(g\) 为所有环的 \(\gcd\),判断存在 \(k\) 使得 \(kg\equiv -s\pmod t\) 即可,这是一次同余方程,有解等价于 \(\gcd(g,t)| {t-s}\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pir pair<int, int>
const int N = 2e5 + 5;
int n, m, u, v, w, T, s, t;
int dfn[N], df, stk[N], low[N], st, bel[N], scc;
int vis[N], xto[N], vis2[N], tox[N], GCD[N];
vector<pir> to[N], to2[N];
inline void dfs(int u){
low[u] = dfn[u] = ++df, stk[++st] = u;
for(auto _ : to[u]){
int v = _.first, w = _.second;
if(!dfn[v]) dfs(v), low[u] = min(low[u], low[v]);
else if(!bel[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u]){
int tp; ++scc;
do{
tp = stk[st--];
bel[tp] = scc;
}while(tp ^ u);
}
}
inline void calc(int id, int rt){
queue<int> q;
q.push(rt), vis[rt] = true;
while(!q.empty()){
int u = q.front(); q.pop();
for(auto _ : to[u]){
int v = _.first, w = _.second;
if(!vis[v] && bel[v] == id)
xto[v] = xto[u] + w, vis[v] = true, q.push(v);
}
}
queue<int> q2;
q2.push(rt), vis2[rt] = true;
while(!q2.empty()){
int u = q2.front(); q2.pop();
GCD[id] = __gcd(GCD[id], xto[u] + tox[u]);
for(auto _ : to2[u]){
int v = _.first, w = _.second;
if(bel[v] == id){
GCD[id] = __gcd(GCD[id], tox[u] + w + xto[v]);
if(!vis2[v])
tox[v] = tox[u] + w, vis2[v] = true, q2.push(v);
}
}
}
}
signed main(){
cin >> n >> m;
for(int i = 1; i <= m; ++i) scanf("%lld%lld%lld", &u, &v, &w), to[u].push_back({v, w}), to2[v].push_back({u, w});
for(int i = 1; i <= n; ++i) if(!dfn[i]) dfs(i);
for(int i = 1; i <= n; ++i) if(!vis[i]) calc(bel[i], i);
cin >> T;
while(T--){
scanf("%lld%lld%lld", &v, &s, &t);
if((t - s) % __gcd(GCD[bel[v]], t) == 0) puts("YES");
else puts("NO");
}
return 0;
}

浙公网安备 33010602011771号