首页 > 技术文章 > 「CF724G」Xor-matic Number of the Graph「线性基」

hongzy 2019-07-28 10:35 原文

题意

求所有点对\(u,v\)\(u\)\(v\)所有不同的异或路径的异或值之和,对\(10^9+7\)取模

题解

求出一个dfs树,那么\(u\)\(v\)的路径一定是树上路径异或一些环。这些环只可能是返祖边构成的,我们把所有环存到线性基里。

先把每一位拆开,答案变为:\(\sum_{i = 0}^{60} 2^i f(i)\),其中\(f(i)\)表示所有满足要求的路径中,第\(i\)位是\(1\)的路径个数

考虑\(f(i)\)怎么求。枚举\(u\)\(v\),我们假设dfs的时候预处理了一个数组\(d\),表示到根的异或值

\(b\)表示线性基里异或得到的数,那么问题转换为\(d[u] \text{ xor } d[v] \text{ xor } b\)有多少种情况第\(i\)位为\(1\)

再令\(x,y\)分别表示\(d[u],d[v]\)的二进制第\(i\)位,令线性基中第\(i\)位是\(1\)的个数为\(c_1\),第\(i\)位是\(0\)的个数为\(c_0\)

\(c_1=0\),意味着线性基对答案没有贡献,随便取,对\(f(i)\)的贡献是\(2^{c_0}[x \text{ xor } y = 1]\)

\(c_1>0\),则线性基有贡献。\(x \text{ xor } y = 1\)\(c_1\)要取奇数个,否则取偶数个

根据组合数学,\(n\)个数里面选偶数个数和选奇数个数的方案数都是\(2^{n-1}\)

所以这种情况对\(f(i)\)的贡献是\(2^{c_0}2^{c_1-1}\)

上面的式子综合一下,\(f(i) = \sum_{1\leq u < v \leq n} ...\)(不写了)

然后我们把枚举的过程省掉。第一种情况等价于求多少对\(u,v\)异或起来第\(i\)位是\(1\),直接记录\(cnt[i]\)表示\(d[u]=i\)\(u\)个数,然后答案就是\(\frac{cnt[i] (n - cnt[i])}{2}\)。第二种情况实际上是所有点对都适用,即为\(\frac{n(n-1)}{2}\)

这题就做完了,不过要注意一下图不一定连通,每个连通块分别求,上面公式中的\(n\)也要改为块的大小

#include <algorithm>
#include <cstdio>
using namespace std;

typedef long long ll;

const int N = 4e5 + 10;
const int mo = 1e9 + 7;

struct Edge {
	int v, nxt; ll w;
} e[N];
int n, m, hd[N], ec;
void clr() {
	fill(hd + 1, hd + n + 1, -1); ec = 0;
}
void add(int u, int v, ll w) {
	e[ec] = (Edge) {v, hd[u], w}; hd[u] = ec ++;
}

namespace base {

ll b[61];
int sz, cnt[61];

void clr() {
	sz = 0;
	fill(b, b + 61, 0);
	fill(cnt, cnt + 61, 0);
}

void ins(ll x) {
	for(int i = 60; i >= 0; i --) {
		if(x & (1ll << i)) {
			if(b[i]) x ^= b[i];
			else {
				b[i] = x; sz ++;
				for(int j = i - 1; j >= 0; j --) if(b[i] >> j & 1) b[i] ^= b[j];
				for(int j = i + 1; j <= 60; j ++) if(b[j] >> i & 1) b[j] ^= b[i];
				break ;
			}
		}
	}
}

void solve() {
	for(int i = 60; i >= 0; i --) if(b[i])
		for(int j = i; j >= 0; j --) if(b[i] >> j & 1) cnt[j] ++;
}

}

ll d[N], cnt[61], bo;
bool vis[N];
void dfs(int u, int pre = -404) {
	vis[u] = 1; bo ++;
	for(int i = 60; i >= 0; i --)
		if(d[u] & (1ll << i)) cnt[i] ++;
	for(int i = hd[u]; ~ i; i = e[i].nxt) if(i != pre) {
		int v = e[i].v; ll w = e[i].w;
		if(!vis[v]) {
			d[v] = d[u] ^ w;
			dfs(v, i ^ 1);
		} else {
			base :: ins(w ^ d[u] ^ d[v]);
		}
	}
}
int main() {
	scanf("%d%d", &n, &m); clr();
	int u, v; ll w;
	for(int i = 1; i <= m; i ++) {
		scanf("%d%d%I64d", &u, &v, &w);
		add(u, v, w); add(v, u, w);
	}
	ll ans = 0;
	for(int u = 1; u <= n; u ++) if(!vis[u]) {
		base :: clr(); bo = 0; fill(cnt, cnt + 61, 0);
		dfs(u); base :: solve();
		for(int i = 0; i <= 60; i ++) {
			ll res = 0;
			if(!base :: cnt[i])	{
				int c0 = base :: sz - base :: cnt[i];
				res = (1ll << c0) % mo * (cnt[i] * (bo - cnt[i]) % mo) % mo;
			} else {
				res = (1ll << (base :: sz - 1)) % mo * (bo * (bo - 1) / 2 % mo) % mo;
			}
			if(res) (ans += (1ll << i) % mo * res % mo) %= mo;
		}
	}
	printf("%d\n", (int) ans);
	return 0;
}

推荐阅读