【CF724G】Xor-matic Number of the Graph

题目

题目链接:https://codeforces.com/problemset/problem/724/G
给你一个无向图,有 \(n\) 个顶点和 \(m\) 条边,每条边上都有一个非负权值。
我们称一个三元组 \((u,v,s)\) 是有趣的,当且仅当对于 \(u,v\in [1,n]\) ,有一条从 \(u\)\(v\) 的路径(可以经过相同的点和边多次),其路径上的权值异或和为 \(s\) 。对于一条路径,如果一条边经过了多次,则计算异或和时也应计算多次。不难证明,这样的三元组是有限的。
计算所有有趣的三元组中 \(s\) 的和对于 \(10^9+7\) 的模数。
\(n\leq 10^5,m\leq 2\times 10^5,\text{边权}\leq 10^{18}\)

思路

把每一个连通块单独拎出来做。
我们知道,\(x\)\(y\) 的任意一条路径异或和可以看作一条 \(x\)\(y\) 的简单路径的异或和再异或上若干个环。那么随便找一棵现在处理的连通块的生成树,对于非树边,都可以与一些树边组合形成环。把这些环的异或和扔进线性基里。
发现每一位是互不影响的,考虑计算每一位的贡献。
对于第 \(i\) 位,假设存在一个环的第 \(i\) 位是 \(1\),那么对于任意两个点,他们之间一定存在一条路径的第 \(i\) 位是一。此时线性基中除了第 \(i\) 位以外所有元素都可以选择,贡献就是

\[\binom{n}{2}\times 2^{\text{siz}-1}\times 2^{i} \]

其中 \(\text{siz}\) 是线性基大小。
如果不存在任意一个环的第 \(i\) 位是 \(1\),我们记 \(\text{dis}_x\) 表示该连通块生成树的根到 \(x\) 路径的异或和,那么只有 \(\text{dis}\)\(0\) 的和 \(\text{dis}\)\(1\) 的之间才能产生贡献,此时线性基中任意元素都可以选择,贡献为

\[\text{cnt}_i(n-\text{cnt}_i)\times 2^{\text{siz}}\times 2^{i} \]

其中 \(\text{cnt}_i\) 表示 \(\text{dis}\)\(i\) 位为 \(1\) 的数量。
时间复杂度 \(O((n+m)\log a)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=400010,LG=60,MOD=1e9+7;
int n,m,tot,head[N],cnt[LG+1];
ll ans,dis[N];
bool vis[N];

struct VectorSpace
{
	int siz;
	ll c[LG+1];
	bool flag[LG+1];
	
	void ins(ll x)
	{
		if (!x) return;
		for (int i=0;i<=LG;i++)
			if (x&(1LL<<i)) flag[i]=1;
		for (int i=LG;i>=0;i--)
			if (x&(1LL<<i))
			{
				if (!c[i]) { c[i]=x; siz++; break; }
				x^=c[i];
			}
	}
	
	void clr()
	{
		memset(flag,0,sizeof(flag));
		memset(c,0,sizeof(c));
		siz=0;
	}
}vs;

struct edge
{
	int next,to;
	ll dis;
}e[N];

void add(int from,int to,ll dis)
{
	e[++tot]=(edge){head[from],to,dis};
	head[from]=tot;
}

void dfs(int x,ll val)
{
	dis[x]=val; vis[x]=1; m++;
	for (int i=0;i<=LG;i++)
		if (val&(1LL<<i)) cnt[i]++;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (!vis[v]) dfs(v,val^e[i].dis);
			else vs.ins(val^dis[v]^e[i].dis);
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	{
		int x,y; ll z;
		scanf("%d%d",&x,&y);
		scanf("%lld",&z);
		add(x,y,z); add(y,x,z);
	}
	for (int i=1;i<=n;i++)
		if (!vis[i])
		{
			memset(cnt,0,sizeof(cnt));
			vs.clr(); m=0;
			dfs(i,0);
			for (int i=0;i<=LG;i++)
				if (vs.flag[i])
					ans=(ans+(1LL*m*(m-1)/2%MOD)*((1LL<<vs.siz-1)%MOD)%MOD*((1LL<<i)%MOD))%MOD;
				else
					ans=(ans+(1LL*cnt[i]*(m-cnt[i])%MOD)*((1LL<<vs.siz)%MOD)%MOD*((1LL<<i)%MOD))%MOD;
		}
	printf("%lld",ans);
	return 0;
}
posted @ 2021-04-06 17:46  stoorz  阅读(75)  评论(0)    收藏  举报