[图灵杯 2025] T2. Bougainvillea
前言
利用率
看看时间
下课上课都别颓了, 好好搞, 下课干点自己想干的事
缓慢耐心
思路
当 \(w = 0\) 时, 相当于询问是否能通过 \(0\) 权边联通 \(u, v\), 这是好做的
考虑特殊性质
\(o = 1\)
显然直接用 \(w = 0\) 的方法即可
\(o = 2\)
相当于询问能否找到一条 \(u, v\) 之间的偶数长度路径
首先, \(u, v\) 应当在一个联通块内, 我们任意找到这个联通块中的一个奇环, 假设奇环上有一个点 \(c\), 那么我们找一个 \(u \to c \to v\) 的路径, 如果这个路径已经是偶数就已经存在, 否则填上奇环上的边也存在
总的来讲, 我们只需要判断这个联通块是否有奇环即可, 如果有那么必定存在, 如果没有证明这是一个二分图, 二分图的一个性质是相同集合之间只有偶数路径, 不同集合之间只有奇数路径, 判断一下即可
另外二分图的集合划分可以用黑白染色来实现, 是唯一确定的
\(o = 3\)
首先考虑 \(w\)
\(w = 0\) 的情况已经讨论过了
\(w = 1\) 的情况等价于联通
\(w \geq 2\) 的情况下, 不妨令 \(w = kx (k\,\)是奇数\()\), 那么此时我们只需要在 \(w = x\) 的答案上往返 \(k\) 次即可
于是问题进一步简化为, \(w = 2^k (k \geq 1)\), 询问是否存在 \(u \to v\) 的长度为 \(2^k\) 倍数的路径
考虑能不能构造出 \(u \to v\) 的长度为 \(2^k\) 倍数的路径
注意到直接对路径做乘法不太行了, 考虑对路径做加法
考虑现在我们有一条长度为偶数的简单路径 \(u \to v\), 因为如果其为奇数, 我们任意调整只能往上加偶数\((\)必定来回\()\)
于是现在我们有 \(len \equiv 2l \pmod{2^k}\), 显然我们可以知道 \(k\) 是偶数, 于是我们一定可以通过挂一条 \(2t \times w\) 的路径上去, 使得 \(len + 2tw \equiv 2l + (2tw \bmod 2^k) \pmod{2^k}\)
不难发现我们需要使得 \(2tw \bmod 2^k = 2^k - 2l\), 也就是 \(2tw \equiv 2^k - 2l \pmod{2^k}\)
根据模运算的性质, \(tw \equiv 2^{k - 1} - l \pmod{2^{k - 1}}\), 这是一个经典的一元线性同余方程, 我们不难发现只要 \(\gcd(w, 2^{k - 1}) \mid 2^{k - 1} - l\) 就一定有解
显然当 \(w, 2^{k - 1}\) 互质时一定有解, 也就是 \(w\) 为奇数一定有解, 不难发现如果存在奇数边就行了
如果不存在呢?
考虑现在所有边都是偶数边, 不妨强制使得每一条边除以所有边的 \(\gcd\), 这样一定存在奇数边, 然后在类似的做即可
完整解法
问题进一步简化为, \(w = 2^k (k \geq 1)\), 询问是否存在 \(u \to v\) 的长度为 \(2^k\) 倍数的路径
先二分图染色, 如果无法染色存在奇环
否则, 如果在不同集合一定无解, 否则存在奇边
还是考虑只要 \(\gcd(w, 2^{k - 1}) \mid 2^{k - 1} - l\) 就一定有解, 那么把奇边吃下去就好了
以上是题解做法, 现在开始参考着自己做一遍
首先不连通的情况提前判断
\(o = 1\)
因为 \(0\) 的倍数仅有 \(0\), 所以我们需要用 \(0\) 权边将 \(u, v\) 连通, 直接并查集可以做到 \(\mathcal{O} (m \log n + q)\)
这是简单的
\(o = 2\)
考虑要找一个经过边数为偶数的路径
套路的想到二分图
二分图染色失败
此时存在奇环
我们设奇环上一点 \(c\), 那么我们找到一条 \(u \to c \to v\) 的路径, 如果它已经是偶数长度的, 那么存在, 否则在奇环上绕一圈, 把它变成偶数的
因此无论如何都存在
二分图染色成功
二分图染色之后, 同集合之间只有偶数长度的路径, 不同集合之间只有奇数长度的路径
直接判断即可
题目中的性质
对于 , 等价于 权边连通
对于 , 等价于联通
对于 , 不妨令 是奇数, 那么此时我们只需要在 的答案上往返 次即可, 等价于
不难发现任意问题可以被尽量拆成 , 我们只需要解决 的问题
\(o = 3\)
考虑此时是在森林上的情况
性质
对于简单路径 , 我们想要走到其他的点, 一定需要来回此时不存在环路
因此可以把问题看做在 的简单路径下面称为始路上添加一些路径, 这些路径都来回走若干遍之后挂上去
因此如果始路的长度为 , 挂上去的路径总长度为 , 那么总长度为
我们只处理 \(w = 2^x\) 的问题, 其他情况要么转化, 要么直接解决
也就是求 \(l_s + 2l_a \equiv 0 \pmod{2^x}\) 的存在性
当 \(l_s\) 为奇数时直接无解, 否则需要使得 \(2l_a \equiv -l_s \pmod{2^x}\)
我们现在需要找到合法的 \(\displaystyle l_a = \sum_{r \in \mathbb{L}} t_rl_r\), 为了方便处理, 我们可以转化成假设有一条路径长度为 \(l_r\), 那么我们可以使 \(l_a \gets l_a + t_rl_r\) 从而使得 \(2l_a \equiv -l_s \pmod{2^x}\)
注意到此时 \(t_rl_r\) 对式子的影响为 \(2t_rl_r \bmod 2^x\)
事实上我们不难观察到, 这一条路径就足够构造答案了
一种简单的构造方式是找到一个奇数 \(l_r\)
不存在仅当所有的树边长度均为偶数
一个很精妙的想法是, 把所有边表示为 \(k \times 2^p\), 其中 \(k\) 为奇数, 不妨找到所有边的 \(p\) 的最小值 \(p_m\), 然后把每条边变成 \(k \times 2^{p - p_m}\) 并把问题转化为 \(w = 2^{x - p_m}\) 的问题
不难证明此时一定存在奇数边并且与原问题完全等价
正解做法
考虑现在我们还是任取一条 \(u \to v\) 的路径, 记作始路, 长度为 \(l_s\)
现在我们的增路有两种情况: 偶环\((\)包含了路径来回走的情况\()\)和奇环
先进行一次二分图染色简化问题
二分图染色失败
此时存在奇环
我们设奇环上一点 \(c\), 那么我们找到一条 \(u \to c \to v\) 的路径作为始路, 长度为 \(l_s\)
现在我们要使得 \(l_s + l_a \equiv 0 \pmod{2^x}\), 等价于找到 \(l_a \equiv -l_s \pmod{2^x}\) 的存在性
不难发现设奇环长度为 \(l_c\), 我们可以把 \(l_a \gets l_a + l_c\), 对式子的影响是 \(l_c \bmod 2^x\)
不难发现一个奇环就足够构造答案了
因此此时必然有解
二分图染色成功
如果不在一个集合, \(u, v\) 之间一定只存在奇数路径, 无解
否则始路的长度 \(l_s\) 一定是偶数, 我们想要找到 \(l_a \equiv -l_s \pmod{2^x}\) 的存在性
因为不存在奇环, 所以不妨修改成 \(2l_a \equiv -l_s \pmod{2^x}\), 注意后面的 \(l_a\) 定义改变
现在我们的增路 \(l_c\) 贡献为 \(2l_c \bmod 2^x\), 注意后面的 \(l_c\) 定义改变
类似 \(o = 3\) 中的分析可以得到, 只要存在 \(l_c\) 为奇数即可
类似的, 如果存在一条奇数边 \((\mu, \vartheta)\), 我们首先找到 \(u \to \mu \to v\) 的路径, 然后再在这条奇数边上来回走即可
如果不存在奇数边, 我们仍然可以类似 \(o = 3\) 中的做法, 把它强行变成一个拥有奇数边的问题
实现
首先把问题转化成一定有奇数边的情况
然后二分图染色, 做一下就好了
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long i64;
constexpr i64 max_bit = 1LL << 62;
int n, m, o;
struct Edge {
int v; i64 w;
};
vector<vector<Edge>> G;
vector<int> comp, comp0, col, conf, vis;
// component id, color, conflict
vector<i64> g(n + 1);
// lowbit of all edges in the component
int cur;
i64 lowbit(i64 x) {
return x & -x;
}
void dfs(int u) {
comp[u] = cur;
for(auto [v, w]: G[u]) {
if(w != 0) {
i64 x = lowbit(w);
g[cur] = min(g[cur], x);
}
if(!comp[v]) {
dfs(v);
}
}
}
void dfs1(int u) {
vis[u] = 1;
for(auto [v, w]: G[u]) {
if(!vis[v]) {
col[v] = col[u] ^ (int)((w / g[cur]) & 1);
dfs1(v);
} else if(col[v] != (col[u] ^ (int)((w / g[cur]) & 1))) {
conf[cur] = 1;
}
}
}
void dfs0(int u) {
comp0[u] = cur;
for(auto [v, w]: G[u]) {
if(w == 0 && !comp0[v]) dfs0(v);
}
}
void prework() {
comp0.resize(n + 1);
for(int i = 1; i <= n; i++) {
if(!comp0[i]) cur++, dfs0(i);
}
cur = 0, comp.resize(n + 1), vis = col = conf = comp;
g.resize(n + 1, max_bit);
for(int i = 1; i <= n; i++) {
if(!comp[i]) {
cur++, dfs(i);
if(g[cur] != max_bit) {
dfs1(i);
}
}
}
}
bool query(int u, int v, i64 w) {
if(w == 0) {
return comp0[u] == comp0[v];
}
if(comp[u] != comp[v]) {
return false;
}
if(w & 1) {
return true;
}
int id = comp[u];
i64 x = lowbit(w);
if(x <= g[id] || conf[id]) {
return true;
}
return col[u] == col[v];
}
int main() {
cin.tie(nullptr) -> sync_with_stdio(false);
int q;
cin >> n >> m >> q >> o;
G.resize(n + 1);
for(int i = 1, u, v; i <= m; i++) {
i64 w;
cin >> u >> v >> w;
G[u].push_back({v, w}), G[v].push_back({u, w});
}
prework();
for(int i = 1, u, v; i <= q; i++) {
i64 w;
cin >> u >> v >> w;
cout << (query(u, v, w) ? "bougain" : "villea") << '\n';
}
return 0;
}
总结
图上奇偶性往往要想到二分图
- 二分图染色失败的时候, 一定存在奇环
- 二分图染色成功的时候, 一定存在奇边
一元线性同余方程和二元线性丢番图方程是等价的
全为偶数这一性质: \(\gcd\) 一定 \(>1\) 且为偶数, 如果全体除以这个 \(\gcd\) 的 \(2\) 的幂次部分, 那么剩下一定存在奇数

浙公网安备 33010602011771号