CF724G Xor-matic Number of the Graph
回到这题。我们发现连通块的贡献独立,也就是说只要对每个连通块单独处理再求和就行了。
接下来我们关心单个连通块怎么做。由于题目让我们求的是所有
假设当前处理到第
-
边权异或和的第
位为 的路径数(记为 )。根据 CF845G 那题中阐述的引理,对于一条路径
的边权异或和,它等价于 。于是它变成了找
的点对数。即
,其中 是当前连通块大小。 -
边权异或和的第
位为 的路径数(记为 )。即
。当然也可以用容斥求得:
。
接下来就可以直接统计答案了。分两种情况:
-
假设当前线性基内存在一个数,满足它的第
位为钦定线性基大小为
,那么对答案的贡献为 。其中
那一项采用了一个经典的组合套路:我们在线性基中任意地取出一个第 位为 的元素。对于其它元素任意选(有 种方案),而选出的元素则根据我们最终想得到的值来决定选不选。 -
不存在这样一个元素
显然只有那些
的点对会在第 位产生贡献。此外,线性基中的每个元素选和不选都是合法的。钦定线性基大小为
,那么贡献为 。
点击查看代码
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】