CF724G Xor-matic Number of the Graph
给你一个无向图,有n个顶点和m条边,每条边上都有一个非负权值。
我们称一个三元组 \((u,v,s)\) 是有趣的,当且仅当对于 \(u,v\in [1,n]\) ,有一条从 \(u\) 到 \(v\) 的路径(可以经过相同的点和边多次),其路径上的权值异或和为 \(s\) 。对于一条路径,如果一条边经过了多次,则计算异或和时也应计算多次。不难证明,这样的三元组是有限的。
计算所有有趣的三元组中 \(s\) 的和对于 \(10^9+7\) 的模数
首先考虑如何计算答案,枚举每一条简单路径(可以写成\(dis_u\oplus dis_v\),\(dis\)表示到\(1\)号点的异或和),然后和所有其他的环都能作为答案。
而这样子并不容易做,我们注意到线性基的一个性质。
假设该线性基的元素个数为\(cnt\)个,那么该线性基可以表示\(2^{cnt}\)个不同的数,而如果线性基的第\(x\)位为\(1\),那么有\(2^{cnt-1}\)个数的第\(x\)为\(1\)。
这启发我们去对每一位分开考虑计算贡献。
如果线性基第\(x\)位为\(1\),那么不管\(u,v\)怎么选都能使异或和第\(x\)位为\(1\),那么贡献为\(2^x2^{cnt-1}\begin{pmatrix}n\\2\end{pmatrix}\)。
如果线性基第\(x\)位不为\(1\),那么找到\(dis_u\)的第\(x\)位的个数\(s\),那么需要选一个第\(x\)为\(1\)的端点和一个第\(x\)不为\(1\)的端点,而线性基上的\(2^{cnt}\)个数可以随便选,贡献为\(2^x2^{cnt}s(n-s)\)。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 2e5;
const int P = 1e9 + 7;
using namespace std;
struct edges
{
int to;
long long cost;
}edge[N * 2 + 5];
int n,m,head[N + 5],nxt[N * 2 + 5],edge_cnt,vis[N + 5],cnt,ans,nn,sm[100];
long long dis[N + 5],p[100];
void add_edge(int u,int v,long long w)
{
edge[++edge_cnt] = (edges){v,w};
nxt[edge_cnt] = head[u];
head[u] = edge_cnt;
}
void ins(long long x)
{
for (int i = 62;i >= 0;i--)
if (x >> i & 1)
{
if (!p[i])
{
p[i] = x;
cnt++;
break;
}
x ^= p[i];
}
}
void dfs(int u,long long cnt)
{
vis[u] = 1;
nn++;
dis[u] = cnt;
for (int i = 62;i >= 0;i--)
if (dis[u] >> i & 1)
sm[i]++;
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i].to;
long long w = edge[i].cost;
if (!vis[v])
dfs(v,cnt ^ w);
else
ins(dis[u] ^ dis[v] ^ w);
}
}
void calc(int st)
{
memset(p,0,sizeof(p));
memset(sm,0,sizeof(sm));
cnt = 0;
nn = 0;
dfs(st,0);
for (int i = 62;i >= 0;i--)
{
int fl = 0;
for (int j = 62;j >= 0;j--)
if (p[j] >> i & 1)
{
fl = 1;
break;
}
if (fl)
ans += 1ll * nn * (nn - 1) / 2 % P * ((1ll << i) % P) % P * ((1ll << cnt - 1) % P) % P,ans %= P;
else
ans += (1ll << cnt) % P * ((1ll << i) % P) % P * sm[i] % P * (nn - sm[i]) % P,ans %= P;
}
}
int main()
{
scanf("%d%d",&n,&m);
int u,v;
long long w;
for (int i = 1;i <= m;i++)
{
scanf("%d%d%lld",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
for (int i = 1;i <= n;i++)
if (!vis[i])
calc(i);
cout<<ans<<endl;
return 0;
}