CF724G Xor-matic Number of the Graph

题目链接

不妨先看一道更为基础的题:CF845G以及它的题解

回到这题。我们发现连通块的贡献独立,也就是说只要对每个连通块单独处理再求和就行了。

接下来我们关心单个连通块怎么做。由于题目让我们求的是所有 s 之和而不是三元组个数,故而考虑拆贡献(对每一位分别考虑)。之后,就可以沿用 CF845G 那题利用线性基来维护路径的思路来计数了。

假设当前处理到第 i 位、有 n0u 满足 disu[i]=1、有 n1u 满足 disu[i]=0disu[i] 表示 disu 这个数二进制的第 i 位是多少)。那么我们根据组合常识不难求出以下值:

  • 边权异或和的第 i 位为 0 的路径数(记为 p0)。

    根据 CF845G 那题中阐述的引理,对于一条路径 uv 的边权异或和,它等价于 disudisv

    于是它变成了找 (disudisv)[i]=1 的点对数。

    s(s1)2,其中 s 是当前连通块大小。

  • 边权异或和的第 i 位为 1 的路径数(记为 p1)。

    n0×n1

    当然也可以用容斥求得:n(n1)2((p02)+(p12))

接下来就可以直接统计答案了。分两种情况:

  1. 假设当前线性基内存在一个数,满足它的第 i 位为 1

    钦定线性基大小为 sz,那么对答案的贡献为 p×2sz1×2i

    其中 2sz1 那一项采用了一个经典的组合套路:我们在线性基中任意地取出一个第 i 位为 1 的元素。对于其它元素任意选(有 2sz1 种方案),而选出的元素则根据我们最终想得到的值来决定选不选。

  2. 不存在这样一个元素

    显然只有那些 (disudisv)[i]=1 的点对会在第 i 位产生贡献。此外,线性基中的每个元素选和不选都是合法的。

    钦定线性基大小为 sz,那么贡献为 p1×2sz×2i

点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const ll mod = 1e9 + 7, inv2 = 500000004;
struct E{int v; ll w;}; vector<E> e[N];
int n, m, sz, vis[N]; vector<ll> s;
ll ans, dis[N], sum, a[65];
void insert(ll x){
	FR(i, 62, 0) if((x >> i) & 1){
		if(!a[i]){sum |= a[i] = x; sz++; return;}
		else x ^= a[i];
	}
}
ll query(ll ret = 0){
	FR(i, 62, 0){
		ll n[2] = {0}, p1, p;
		ll c = (1ll << i) % mod * ((1ll << sz) % mod) % mod;
		for(const ll &x: s) n[(x >> i) & 1]++;
		p1 = n[0] * n[1] % mod;
		p = s.size() * (s.size() - 1ll) / 2 % mod;
		if((sum >> i) & 1)
			(ret += p * c % mod * inv2) %= mod;
		else (ret += p1 * c) %= mod;
	}
	return ret;
}
void dfs(int u, int fa){
	vis[u] = 1, s.emplace_back(dis[u]);
	for(const auto &p: e[u]) if(p.v != fa){
		if(!vis[p.v]) dis[p.v] = dis[u] ^ p.w, dfs(p.v, u);
		else insert(dis[u] ^ dis[p.v] ^ p.w);
	}
}
int main(){
    scanf("%d%d", &n, &m);
    FL(i, 1, m){
        int u, v; ll w;
		scanf("%d%d%lld", &u, &v, &w);
        e[u].emplace_back((E){v, w});
		e[v].emplace_back((E){u, w});
    }
    FL(i, 1, n) if(!vis[i]){
    	vector<ll>().swap(s);
    	fill(a, a + 63, 0ll), sum = sz = 0;
    	dfs(i, 0), ans = query(ans);
	}
	printf("%lld\n", ans);
    return 0;
}
posted @   徐子洋  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示