题解:CF724G Xor-matic Number of the Graph

节选自:线性代数学习笔记(二):线性基

我们先不考虑这道题是在图上操作,因此我们先来求不同异或和的和。

由于线性基中任意一组基异或起来都互不相同,这大大简化了这个问题,我们只需要统计即可,不需要考虑去重。

我们考虑按位算贡献。假设线性基中有 \(tot\) 个基,其中有 \(k (k > 0)\) 个基的第 \(i\) 位为 \(1\),那么就有 \(tot - k\) 个第 \(i\) 位为 \(0\) 的基。这些第 \(i\) 位为 \(0\) 的基无论选不选都对答案不造成贡献,因此最终这一位的答案一定会乘以 \(2^{tot - k}\)

现在我们考虑第 \(i\) 位为 \(1\)\(k\) 个基,只有当从中选出奇数个基时,才会在这一位上留下一个 \(1\),因此答案是 \(\displaystyle\sum_{0 \leq j \leq k \wedge j \bmod 2 = 1} \binom kj\)

我们考虑二项式定理 \((x + y)^k = \displaystyle\sum_{0 \leq j \leq k} \binom kj x^j y^{k - j}\),由于 \(k > 0\),我们把 \(x, y\) 带成 \(-1\)\(1\),那么 \(0 = \displaystyle\sum_{0 \leq j \leq k} \binom kj (-1)^j\),这个式子在 \(j\) 为偶数时为正,\(j\) 为奇数时为负,于是 \(\displaystyle\sum_{0 \leq j \leq k \wedge j \bmod 2 = 1} \binom kj = \sum_{0 \leq j \leq k \wedge j \bmod 2 = 0} \binom kj\)

因为组合数一行之和 \(\displaystyle 2^k\),那么 \(\displaystyle\sum_{0 \leq j \leq k \wedge j \bmod 2 = 1} \binom kj = 2^{k - 1}\),于是这一位的答案就是 \(2^{tot - 1}\)

很显然,如果 \(k\)\(0\) 那么答案就是 \(0\),因此如果存在一个最高位为 \(i\) 的基,那么答案就会乘以 \(2^{tot - 1}\),那么答案就是 \(\displaystyle\prod_{i = 0}^{\log n} 2^{tot - 1} [w_i \neq 0]\)

回到这道题目,我们需要求不同的路径异或和的和。参考例题十一,我们把所有环插入线性基中,再找到一条主路径,那么最终答案就是线性基中的答案异或上主路径长度,但 \(u\)\(v\) 都是不固定的,如果一对一对枚举,复杂度直接炸了,考虑如何优化。

还是考虑异或相等为 \(0\) 的情况。如果我们随便找一个点作为起点,记录到 \(u\) 的距离 \(dis_u\) 和到 \(v\) 的距离 \(dis_v\),那么 \(u\)\(v\) 的距离就是 \(dis_u \operatorname{xor} dis_v\)

那么现在问题就变成了在集合 \(\{dis_i\}\),中任意选两个数字 \(dis_u, dis_v\),我们求出线性基中异或和的和,再异或上 \(dis_u\)\(dis_v\),加入到答案中就行了。但这样复杂度依然爆炸。

我们继续考虑按位贡献,我们知道线性基如果有一个数第 \(i\) 位上是 \(1\),那么这一位上就有 \(2^{tot - 1}\) 种方式凑出 \(1\)\(2^{tot - 1}\) 种方式凑出 \(0\)。因此无论 \(\{dis_i\}\) 中选出两个数的异或和的第 \(i\) 位为 \(1\) 或者 \(0\),对答案的贡献都是 \(2^{tot - 1}\)。假设 \(\{dis_i\}\) 的大小为 \(siz\),那么总贡献就是 \(2^{tot - 1} \times \displaystyle\frac{siz(siz - 1)}{2}\)

如果线性基中没有数字第 \(i\) 位上是 \(1\),那么无论多少个数异或起来都是 \(0\),因此答案需要乘以 \(2^{tot}\)。其次选出的 \(dis_u\)\(dis_v\) 的第 \(i\) 位上必须一个是 \(0\),一个是 \(1\)。我们假设 \(\{dis_i\}\) 中有 \(cnt\) 个数字第 \(i\) 位上为 \(1\),那么对于答案的贡献就是 \(2^{tot} \times cnt \times (siz - cnt)\)

最后,这道题的图可能不联通,每个连通块分开算即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 9, M = 2e5 + 9, MOD = 1e9 + 7;
struct Edge{
	int v, w, nex;
} e[M << 1];
int head[N], ecnt;
void addEdge(int u, int v, int w){
	e[++ecnt] = Edge{v, w, head[u]};
	head[u] = ecnt;
}
int qpow(int a, int b){
	int res = 1;
	while(b > 0){
		if(b & 1)
			res = res * a % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return res;
}
int dis[N], n, m, ans, sum;
vector <int> vec;
bool vis[N];
struct Basis{
	int w[69], tot;
	void clear(){
		memset(w, 0, sizeof(w));
		tot = 0;
	}
	void insert(int x){
		for(int i = 62; ~i; i--){
			if(x >> i & 1ll){
				if(w[i])
					x ^= w[i];
				else {
					w[i] = x;
					tot++;
					break;
				}
			}
		}
	}
	int calc(){
		int sum = 0, res = 0, siz = vec.size();
		for(int i = 62; ~i; i--)
			sum |= w[i];
		for(int i = 62; ~i; i--){
			int cnt = 0;
			for(int j = 0; j < siz; j++)
				if(vec[j] >> i & 1ll)
					cnt++;
			if(sum >> i & 1ll)
				res = (res + qpow(2, i) % MOD * qpow(2, tot - 1) % MOD * (siz * (siz - 1) / 2 % MOD) % MOD) % MOD;
			else
				res = (res + qpow(2, i) % MOD * qpow(2, tot) % MOD * cnt % MOD * (siz - cnt) % MOD) % MOD;
		}
		return res;
	}
} b;
void dfs(int u){
	vis[u] = true;
	vec.push_back(dis[u]);
	for(int i = head[u]; i; i = e[i].nex){
		int v = e[i].v;
		if(vis[v])
			b.insert(dis[u] ^ dis[v] ^ e[i].w);
		else {
			dis[v] = dis[u] ^ e[i].w;
			dfs(v);
		}
	}
}
signed main(){
	scanf("%lld%lld", &n, &m);
	for(int i = 1; i <= m; i++){
		int u, v, w;
		scanf("%lld%lld%lld", &u, &v, &w);
		addEdge(u, v, w);
		addEdge(v, u, w);
	}
	for(int i = 1; i <= n; i++){
		if(!vis[i]){
			b.clear();
			vec.clear();
			dfs(i);
			ans = (ans + b.calc()) % MOD;
		}
	}
	printf("%lld", ans);
	return 0;    
}
posted @ 2025-03-25 08:44  Orange_new  阅读(17)  评论(0)    收藏  举报