qoj7177
题目链接。求出无向图所有简单环的环长的 \(\operatorname{gcd}\)。
用到了一个我没有听过的东西,感觉还挺有意思。
对于任意无向图,定义一个无向图的环是一组让每个点的度数都是偶数的边集(注意这和一般的定义略有差别)。我们宣称无向图的所有环构成线性空间,这是显然的,两个不同的环的异或还是环,成为环空间。
现在我们拎出来任意一个生成森林,并且宣称所有的仅包含一条非树边的环是该图对应的环空间的一个基。证明是容易的,首先这些环显然不能互相表示,然后显然每个环都可以由这些环通过异或表达出来。故一个 \(n\) 个点 \(m\) 条边的无向图的环空间是非树边数量维的。
定义两条边是等价的,要么他们都是割边,要么在同一个边双,且割掉他们后边双不连通。称为切边等价,两条切边等价当且仅当对于任意环,要么他们同时存在,要么同时不存在。任意一个非割边的切边等价类都可以表示为环空间内所有元素的实系数线性组合。显然本质不同的切边等价类不超过 \(n\) 个(废话)。切边划分可以仅通过环空间的基来划分,这也是正确的。
回到这道题,考虑两个不同的环 \(c_1,c_2\),\(|c_1\cap c_2|=s\),对于答案的贡献为 \(\operatorname{gcd}(|c_1|,|c_2|,|c_1|+|c_2|-2s)\)。注意到 \(s\) 其实代表了某一个切边等价类的权值和,那么我们找到一组基,用树上差分加异或哈希的方式维护等价类即可。
#include<iostream>
#include<random>
#include<chrono>
#include<algorithm>
#include<unordered_map>
const int N = 5005; using ll = long long; std::unordered_map<ll, ll> mp;
std::mt19937_64 rd(std::chrono::system_clock::now().time_since_epoch().count());
int n, m, vs[N]; ll d[N], f[N], rs; std::vector<std::pair<int, int>> g[N];
void dfs(int x, int fa){
vs[x] = 1; for(auto [v, w] : g[x]) if(v != fa)
if(!vs[v]) d[v] = d[x] + w, dfs(v, x), f[x] ^= f[v], mp[f[v]] += w;
else if(d[v]<d[x]){ll k=rd(); f[v]^=k,f[x]^=k,mp[k]+=w; rs=std::__gcd(rs,d[x]-d[v]+w);}
}
int main(){
std::ios::sync_with_stdio(false), std::cin.tie(0);
std::cin >> n >> m; for(int i = 1, x, y, w; i <= m; i++)
std::cin >> x >> y >> w, g[x].push_back({y, w}), g[y].push_back({x, w});
for(int i = 1; i <= n; i++) if(!vs[i]) dfs(i, 0);
for(auto [a, b] : mp) if(a) rs = std::__gcd(rs, b * 2); std::cout << rs << '\n';
}

浙公网安备 33010602011771号