CF1515G Phoenix and Odometers

发布时间 2023-06-20 22:08:40作者: kyEEcccccc

有点神仙的。

题意

给定一张 \(n\) 个点 \(m\) 条边的有向图,有边权,进行 \(q\) 次询问(\(n,m,q\le 2\times 10^5\),边权为不超过 \(10^9\) 的正整数)。

每次询问给定三个参数 \(v,s,t(0\le s<t\le 10^9)\),你需要回答是否存在一条起点终点均为 \(v\) 的路径,满足 \(\text{路径长}+s\equiv 0\pmod t\)

题解

按照经验,这种让你找环的题目一定是让你找到一组基能表示任何其它东西。首先考虑一个结论,你可以在一个环上绕 \(t\) 圈使得这个环等于没绕,同时借此到达任何一个环上节点。那么实际上 \(v\) 所在的强连通分量内部的所有环都能为你所用,而且你不会离开强连通分量。那么相当于在若干张独立的强连通图上,去考虑里面所有可能的环。注意到所有环也都可以绕任意圈,所以你由裴蜀定理,只关心所有环的 \(\gcd\)

接下来,考虑 DFS 树。考虑将一条边 \((u, v)\) 的权值设为它的权值加上 \(u\) 在正图 DFS 树上的深度加上 \(v\) 在反图 DFS 序上的深度。将一个点的权值设为它在正反图上深度之和。容易得到这些都是合法的环长度。接下来,考虑一个任意环,它可以通过每条边的权值,减掉每个点的权值得到(相当于消去了从根走下来和走上去的路径长度)。那么我们就证明了这是一组基,所有环可以被它们组合而来。接下来运用裴蜀定理即可。

代码

// Author: kyEEcccccc

#include <bits/stdc++.h>

using namespace std;

using LL = long long;
using ULL = unsigned long long;

#define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max(a, b))
#define MIN(a, b) ((a) = min(a, b))
#define SZ(a) ((int)((a).size()) - 1)

constexpr int N = 200005;

int n, m, q;
vector<pair<int, LL>> e[N], re[N];
struct Edge { int u, v; LL w; } ed[N];

int dfn[N], low[N], tot_dfn;
int co[N], tot_co;
vector<int> sk; bool insk[N];

void tarjan(int u)
{
	dfn[u] = low[u] = ++tot_dfn;
	sk.push_back(u), insk[u] = 1;
	for (const auto& ee : e[u])
	{
		if (!dfn[ee.first])
		{
			tarjan(ee.first);
			MIN(low[u], low[ee.first]);
		}
		else if (insk[ee.first]) MIN(low[u], low[ee.first]);
	}
	if (low[u] >= dfn[u])
	{
		++tot_co;
		int t;
		do {
			t = sk.back();
			sk.pop_back();
			insk[t] = 0;
			co[t] = tot_co;
		} while (t != u);
	}
}

LL dep1[N], dep2[N];
bool vis1[N], vis2[N];

void dfs(int u, LL d[N], vector<pair<int, LL>> ce[N], bool vis[N])
{
	vis[u] = 1;
	for (const auto& ee : ce[u])
	{
		if (co[ee.first] != co[u] || vis[ee.first]) continue;
		d[ee.first] = d[u] + ee.second;
		dfs(ee.first, d, ce, vis);
	}
}

LL g[N];

void modi(LL &x, LL y)
{
	if (x == -1) x = y;
	x = __gcd(x, y);
}

signed main(void)
{
	// freopen(".in", "r", stdin);
	// freopen(".out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(nullptr);

	cin >> n >> m;
	F(i, 1, m)
	{
		cin >> ed[i].u >> ed[i].v >> ed[i].w;
		e[ed[i].u].push_back({ed[i].v, ed[i].w});
		re[ed[i].v].push_back({ed[i].u, ed[i].w});
	}

	F(i, 1, n) if (!dfn[i]) tarjan(i);
	F(i, 1, n)
	{
		if (vis1[i]) continue;
		dep1[i] = 0;
		dfs(i, dep1, e, vis1);
		dep2[i] = 0;
		dfs(i, dep2, re, vis2);
	}

	memset(g, -1, sizeof (g));
	F(i, 1, n) modi(g[co[i]], dep1[i] + dep2[i]);
	F(i, 1, m)
	{
		if (co[ed[i].u] != co[ed[i].v]) continue;
		modi(g[co[ed[i].u]], dep1[ed[i].u] + dep2[ed[i].v] + ed[i].w);
	}

	cin >> q;
	F(_, 1, q)
	{
		int u, s, t; cin >> u >> s >> t;
		if (g[co[u]] == -1)
		{
			cout << "NO\n";
			continue;
		}
		if (s % __gcd(g[co[u]], (LL)t) == 0) cout << "YES\n";
		else cout << "NO\n";
	}
	
	return 0;
}