【HDU3949 + BZOJ2115 + CF724G】【异或线性基例题】| 倍增 | 第k小异或和 | DFS处理环 |【CGWR】| N

三道关于异或线性基的有趣的题目

[1] HDU 3949. XOR

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5846    Accepted Submission(s): 2041

Tags:异或线性基 倍增


题目描述

(这题目名字真粗暴…)给定一组数,可任意选数求异或和。求所有可能的异或和的第 k k k 小值。



输入

T T T 组。第一行输入组数 T T T

对于每组输入,第一行一个数 N N N 代表数的个数,接下来一行 N N N 个数,即为每个数 w i w_i wi

再接下来一个数 q q q 代表有 Q Q Q 次询问,再接下来一行 Q Q Q 个数,代表每次询问是第几小。


范围: T ≤ 30 , 1 ≤ N ≤ 1 0 4 , 1 ≤ Q ≤ 1 0 4 , 1 ≤ w i ≤ 1 0 18 T \le 30,1 \le N \le 10^4,1 \le Q \le 10^4,1 \le w_i \le 10^{18} T301N1041Q1041wi1018



输出

对于每组输入,先输出一行 Case #$d:,%d 是组数

然后输出 Q Q Q 行,每行一个数代表每次询问的答案值。如果当次询问无效(不存在第 k k k 小)则输出 -1

相邻两组输出之间不用打空行



输入样例

2
2
1 2
4
1 2 3 4
3
1 2 3
5
1 2 3 4 5

输出样例

Case #1:
1
2
3
-1
Case #2:
0
1
2
3
-1




分析


思路分析

板子题。具体怎么求第 k k k 小呢?我们需要把求出来的线性基给处理一下:

  • 1.对于线性基的每一项,要尽量利用其它项把它的非最高位 1 1 1 给消除。(如果根本没法消除就算了)(具体操作的时候一定要从高到低遍历,因为高位异或可能会影响低位)
  • 2.新建一个线性基数组。本来线性基数组的第 i i i 项的含义是最高位 1 1 1 在第 i i i 位的那一项,而现在我们需要另一个数组表示线性基,它的第 i i i 项的意思是这一项是线性基中的第 i i i 大者。

其中第一步的目的是为了使第 k k k 小答案是正确的。经过第一步的处理后可以确保选的基项越多就必定会让异或和越大。

而要理解第二步,不妨来一点 离散化 的思想。第 k k k 小其实和线性基的最高位 1 1 1 在哪一位(具体的值的大小)是无关的,而仅与线性基的个数(相对大小顺序)有关。

比如线性基是 001 , 010 , 100 001,010,100 001010100,显然第 3 ( 11 B ) 3(11B) 3(11B) 小是第 1 ( 1 B ) 1(1B) 1(1B) 项异或上第 2 ( 10 B ) 2(10B) 2(10B) 项,也就是 001 ⊕ 010 = 011 001\oplus 010 = 011 001010=011
而如果线性基是 0000001 , 0000010 , 1000000 0000001,0000010,1000000 000000100000101000000,那第 3 ( 11 B ) 3(11B) 3(11B) 小也是一样的求法,第 1 ( 1 B ) 1(1B) 1(1B) 项异或上第 2 ( 10 B ) 2(10B) 2(10B) 项。

然后做完这两步处理之后(记此时线性基数组是 b [ ] b[] b[]),为什么直接按二进制拆分 k k k 再异或上相应的线性基就可以直接得解了呢?

因为处理完之后可以保证升序顺序是: b [ 0 ] &lt; b [ 1 ] &lt; b [ 0 ] ⊕ b [ 1 ] &lt; b [ 2 ] &lt; b [ 2 ] ⊕ b [ 0 ] &lt; b [ 2 ] ⊕ b [ 1 ] ⊕ b [ 0 ] &lt; b [ 3 ] &lt; . . . b[0]&lt;b[1]&lt;b[0]\oplus b[1]&lt;b[2]&lt;b[2]\oplus b[0]&lt;b[2]\oplus b[1]\oplus b[0]&lt;b[3]&lt;... b[0]<b[1]<b[0]b[1]<b[2]<b[2]b[0]<b[2]b[1]b[0]<b[3]<...

观察规律就不难得到答案了。


小细节

另外这题还有两个小细节:

  • 1.如果最后线性基的项数小于数的个数,说明有的数是可以通过其他数表示的。也就是说第 1 1 1 小是 0 0 0。这种情况要特判一下。
  • 2.记线性基的项数是 n b n_b nb,显然线性基能表示的全集有 2 n b − 1 2^{n_b}-1 2nb1 这么多项(每项都有选或不选两种选择,然后-1是减去都不选的情况),那如果 k &gt; 2 n b − 1 k&gt;2^{n_b}-1 k>2nb1 就说明无解了,此时就输出 -1。

时间复杂度
  • 动态插入线性基, O (   N log ⁡ 2 ( max ⁡ { w i } )   ) O(\ N \log_2(\max\{w_i\})\ ) O( Nlog2(max{wi}) ) 的。
  • 处理线性基, O (   log ⁡ 2 ( max ⁡ { w i } )   ) O(\ \log_2(\max\{w_i\})\ ) O( log2(max{wi}) ) 的。
  • Q 次询问, O (   Q log ⁡ 2 k   ) O(\ Q\log_2k\ ) O( Qlog2k ) 的。
  • 总时间复杂度: O (   T (   N log ⁡ 2 ( max ⁡ { w i } )   +   Q log ⁡ 2 k   )   ) O(\ T(\ N \log_2(\max\{w_i\})\ +\ Q\log_2k\ )\ ) O( T( Nlog2(max{wi}) + Qlog2k ) )




AC代码

// http://acm.hdu.edu.cn/showproblem.php?pid=3949

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define ULL unsigned long long
#define _BS 1048576
char _bf[_BS];
char *__p1=_bf,*__p2=_bf;
#define _IO char *_p1=__p1,*_p2=__p2;
#define _OI __p1=_p1,__p2=_p2;

//#define GC getchar()
#define GC (_p1==_p2&&(_p2=(_p1=_bf)+fread(_bf,1,_BS,stdin),_p1==_p2)?EOF:*_p1++)
#define PC putchar
#define _Q_(x) {register char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;
#define _H_(x) for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define sc(x) _Q_(x)_H_(x)
#define se(x) _Q_(x)else if(_c==EOF)return 0;_H_(x)

template<class T>
void PRT(const T _){if(_<0){PC(45),PRT(-_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}

#define MN 10007
#define ONE 1ull


int main()
{
	ULL tp, k, ans;

	_IO
	int T;
	sc(T)
	for (int CASE=1; CASE<=T; ++CASE)
	{
		ULL base[66] = {0};
		int n;
		sc(n)

		for (int _=0; _<n; ++_)
		{
			sc(tp)

			for (int i=61; i>=0; --i)	// 插入线性基
			{
				if (tp & (ONE << i))
				{
					if (base[i])
						tp ^= base[i];
					else
					{
						base[i] = tp;
						break;
					}
				}
			}
		}

		for (int i=61; i>=0; --i)			// 对于线性基的每一项,将其非最高位1给清0(通过比它小的项跟他xor)
		{
			if (base[i])
			{
				for (int j=i-1; j>=0; --j)	// 从高到低遍历base[i]的低位(0到i-1),如果有1就得异或。【不能从低到高】,因为高位异或可能会改变低位。
					if (base[i] & (ONE << j))
						base[i] ^= base[j];
			}
		}

		int tot = 0;
		for (int i=0; i<=61; ++i)
			if (base[i])
				base[tot++] = base[i];	// 这句话的目的是把线性基都往前挪(删去那些不存在的项)当然如果嫌就地处理不好看也可以开一个新的数组final[],然后final[tot++] = base[i],然后最后查询的时候就是 ans ^= tot[i]
		// 这样做之后,base[i]的意义就不再是最高位1在第i位的基项了,而是第i大的基项

		ULL all = (ONE << tot) - 1;		// 线性基有tot项,总共可以表示2^tot -1个数。(每项都是选或不选两种选择,然后减一是减去谁都不选的情况)
		int q;
		sc(q)
		printf("Case #%d:\n", CASE);
		while (q--)
		{
			sc(k)
			if (tot != n)		// 线性基的项数不等于原数组的项数,说明原数组任意异或是可以表示出0的,第1小肯定是0,就“去掉”这个0(把排名给减减)
				--k;
			if (k > all)		// 根本就不能表示这么多个数
				PC('-'), PC('1'), PC(10);
			else
			{
				ans = 0;
				for (int i=0; i<=61; ++i)
					if (k & (ONE << i))	// 二进制拆分。比如如果k的二进制是100,那当i==2的时候就if成功,也就是说要跳过前两个线性基(前两个线性基base[0]和base[1]可以弄出3个数),第四小的数刚好就是base[2]本身
						ans ^= base[i];					// 再比如k的二进制是101,那当i==0时就进if,ans=base[0],然后i==2的时候进,ans=base[0]^base[2]。显然这就是第5大的数(第四大的数再xor上最小的线性基即base[0])
				UPRT(ans), PC(10);
			}
		}
	}

	return 0;
}




[2] BZOJ 2115. [Wc2011] Xor

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 5829   Solved: 2481

Tags:异或线性基 DFS


题目描述

给定一个无向连通图,顶点编号从 1 1 1 N N N,求从 1 1 1 走到 N N N 的最大路径权值异或和(不一定是简单路径)



输入

1 1 1 组。第一行输入 N N N M M M 分别代表点数和边数,

接下来 M M M 行每行三个数 u u u v v v w w w,代表每条边的起点、终点、边权


范围: N ≤ 5 × 1 0 4 , M ≤ 1 0 5 , 0 ≤ w i ≤ 1 0 18 N \le 5 \times 10^4,M \le 10^5,0 \le w_i \le 10^{18} N5×104M1050wi1018



输出

一个数,从 1 1 1 号点走到 N N N 号点的最大路径权值异或和(不一定是简单路径)



输入样例

5 7 
1 2 2 
1 3 2 
2 4 1 
2 5 1 
4 5 3 
5 3 4 
4 3 2 

输出样例

6

样例图示

在这里插入图片描述





分析


① 无环

首先我们考虑最简单的——无环的情况。由于单纯的回头路是没有意义的(必定会经过每条边奇数次)所以从 1 1 1 走到 N N N 的异或和是唯一的。


② 有环,但环不影响简单路径的唯一性

此时 1 到 N 简单路径仍是唯一的,但是简单路径之外可能有环。于是我们可以从简单路径的某个位置走出去,再绕某个环兜个圈子再回到简单路径上,再去 N N N 点。

画个图就知道:
在这里插入图片描述
显然,sum2 和 sum4 抵消了,总的异或和就是 sum1 ^ sum3 ^ sum5,就是多异或了一个环上的异或和。

由此可以发现,解的集合就是在简单路径的异或和的基础上异或任意多个环的异或和而得到的集合(可以绕不止一个环)。

这时候要找最大值就可以把问题交给 异或线性基 了。


③ 有环,且环影响简单路径的唯一性

此时简单路径也不唯一了。这也好办,为什么呢,因为如果我们把一切环都加进线性基的话,那么包含 1 和 N 点的环也加了

这会发生什么呢?

  • 第一种情况(1 和 N 也在环里面):

在这里插入图片描述
如果 sum2 更大,而我们最先选定的简单路径是 sum1 那条的话,在线性基求最大值的时候,xor 一下这个大环的异或和就把 sum1 xor 成 sum2 了。所以我们仍然可以正确求得最大解。(解的集合还是在某一条(最开始选定的那条)简单路径的异或和的基础上异或任意多个环的异或和而得到的集合)。


  • 第二种情况(不都在环里面,以都不在为例。一个在另一个不在显然可以一样考虑):

在这里插入图片描述
如果一开始选的简单路径是 sum1 ^ sum2 ^ sum4,但是 sum1 ^ sum3 ^ sum4 更大,那在线性基求最大值的时候,xor 一下也可以把 124 xor 成 134。所以我们仍然可以正确求得最大解。(解的集合还是在某一条(最开始选定的那条)简单路径的异或和的基础上异或任意多个环的异或和而得到的集合)。


④ 有环,甚至还有重边(二点环)和自环(一点环)

没问题没问题,这俩情况也都不影响。解的集合还是在某一条(最开始选定的那条)简单路径的异或和的基础上异或任意多个环(也包括二点环和一点环)的异或和而得到的集合。




解题思路

到以上④,所有情况就都考虑完了。解的集合就是在某一条(最开始选定的那条)简单路径的异或和的基础上异或任意多个环(也包括二点环和一点环)的异或和而得到的集合。最大解可通过异或线性基给出。求出所有环的异或和的一组线性基,然后贪心地从最高位考虑要不要 xor 上去即可。

在这里求所有环的异或和可不必用 Tarjan。简单 DFS 即可。找到返祖边的话就说明搜到一个环了,记一下异或和就行。



时间复杂度
  • 动态插入线性基, O (   N log ⁡ 2 ( max ⁡ { d i } )   + M   ) O(\ N \log_2(\max\{d_i\})\ +M\ ) O( Nlog2(max{di}) +M ) 的。
  • 求最大值, O (   log ⁡ 2 ( max ⁡ { d i } )   ) O(\ \log_2(\max\{d_i\})\ ) O( log2(max{di}) ) 的。
  • 总时间复杂度: O (   N log ⁡ 2 ( max ⁡ { d i } )   + M   ) O(\ N \log_2(\max\{d_i\})\ +M\ ) O( Nlog2(max{di}) +M )




AC代码

(想吐槽一下… BZOJ 不给用 constexpr 就算了,还不让用 unordered_map !)

#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <algorithm>
#include <vector>
#include <map>
#include <set>

#define LL long long
#define ULL LL
#define _BS 1048576
char _bf[_BS];
char *__p1=_bf,*__p2=_bf;
#define _IO char *_p1=__p1,*_p2=__p2;
#define _OI __p1=_p1,__p2=_p2;

#ifdef _KEVIN
#define GC getchar()
#else
#define GC (_p1==_p2&&(_p2=(_p1=_bf)+fread(_bf,1,_BS,stdin),_p1==_p2)?EOF:*_p1++)
#endif

#define PC putchar
#define _Q_(x) {register char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;
#define _H_(x) for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define sc(x) _Q_(x)_H_(x)
#define se(x) _Q_(x)else if(_c==EOF)return 0;_H_(x)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define F(i,l,r) for(int i=l;i<r;++i)

template<class T>
void PRT(const T _){if(_<0){PC(45),PRT(-_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}

const int MV(5e4+7), ME(1e5+7), MB(61);

#define ONE 1ull


struct Edge
{
	ULL d;
	int next, v;
} ed[2 * ME];
int head[MV], tot;
#define edd(_u, _v, _d) ed[++tot].next=head[_u], ed[tot].v=_v, ed[tot].d=_d, head[_u]=tot

bool vis[MV];
ULL dis[MV];
ULL base[66];

void insert(ULL v)
{
	if (v)
		for (int i=63-__builtin_clzll(v); i>=0; --i)
		{
			if (v & (ONE << i))
			{
				if (base[i])
					v ^= base[i];
				else
				{
					base[i] = v;
					break;
				}
			}
		}
}


ULL get_max(ULL path_xor)
{
	for (int i=MB; i>=0; --i)
		if ((path_xor ^ base[i]) > path_xor)
			path_xor ^= base[i];

	return path_xor;
}


void dfs(const int u)
{
	for (int i=head[u]; i; i=ed[i].next)
	{
		const int v = ed[i].v;
		if (vis[v])
			insert(dis[u] ^ dis[v] ^ ed[i].d);
		else
			vis[v] = true, dis[v] = dis[u] ^ ed[i].d, dfs(v);
	}
}


int main()
{
	_IO
	
	int V, E, u, v;
	ULL d;
	sc(V)sc(E)
	while (E--)
	{
		sc(u)sc(v)sc(d)
		edd(u, v, d);
		edd(v, u, d);
	}

	vis[1] = true, dfs(1);
	ULL path_xor = dis[V];
	path_xor = get_max(path_xor);
	UPRT(path_xor);

	return 0;
}




[3] Codeforces 724G. Xor-matic Number of the Graph

time limit per test: 2 seconds
memory limit per test: 256 megabytes
input: standard input
output: standard output

Tags:异或线性基 DFS


题目描述

给定一个无向图(不一定连通),顶点编号从 1 1 1 N N N。定义三元组 &lt; u , v , w &gt; &lt;u, v, w&gt; <u,v,w> 表示从 u u u v v v 的一条异或和为 w w w 的路径(不一定是简单路径)(注意如果多次经过了某条边,则异或和中这条边的值也应被异或了相应次数)

求这个图中共有多少个不相等的三元组(两个三元组是相等的当且仅当 u 、 v 、 w u、v、w uvw 均相等)

总数可能很大,对 1 0 9 + 7 10^9 + 7 109+7 取模。



输入

1 1 1 组。第一行输入 N N N M M M 分别代表点数和边数,

接下来 M M M 行每行三个数 u u u v v v w w w,代表每条边的起点、终点、边权


范围: N ≤ 5 × 1 0 5 , 0 ≤ M ≤ 2 × 1 0 5 , 0 ≤ w i ≤ 1 0 18 N \le 5 \times 10^5,0 \le M \le 2\times10^5,0 \le w_i \le 10^{18} N5×1050M2×1050wi1018



输出

一个数,不相等的三元组的总数(对 1 0 9 + 7 10^9 + 7 109+7 取模)



输入样例 1

4 4
1 2 1
1 3 2
2 3 3
3 4 1

输出样例 1

12

输入样例 2

4 4
1 2 1
2 3 2
3 4 4
4 1 8

输出样例 2

90

输入样例 3

8 6
1 2 2
2 3 1
2 4 4
4 5 5
4 6 3
7 8 5

输出样例 3

62




分析

像这种求 图上异或和xxx的路径总条数,常常都是要 按位考虑对总和的贡献


首先根据上面的第二题,我们已经得出结论:

  • 在同一个连通分量内部,某两点 u , v u, v u,v 间的所有可能路径的异或和的集合,等价于 d i s [ u ] dis[u] dis[u] 异或 d i s [ v ] dis[v] dis[v] 再异或上 { \{ {环异或和的线性基 } \} } 中的任意多项而得到的集合(其中 d i s [ i ] dis[i] dis[i] 表示从该连通分量的任意一点出发产生的 DFS 树上, i i i 点到源点的路径异或和)。

那对于某一个有 n n n 个点的连通分量 { C C } \{CC\} {CC},假设我们已经求出了 { \{ {环异或和的线性基 } \} } ,接下来怎么办呢?显然不能暴力地枚举所有点对。我们应该 按位考虑对总和的贡献。(记线性基的大小为 s i z e size size)那么对于第 k k k 位:

  • 1. 如果这一位能通过线性基表示出来(线性基中有某一项的第 k k k 位是 1 1 1。注意这个 1 1 1 不一定非得是最高位 1 1 1),那么线性基能表示的全体空间中必恰有 2 s i z e − 1 2^{size-1} 2size1 个数的第 k k k 位含 1 1 1,必恰有 2 s i z e − 1 2^{size-1} 2size1 个数的第 k k k 位不含 1 1 1
    • 那么 ∀ &lt; u , v &gt; ∈ { C C } \forall&lt;u, v&gt; \in \{CC\} <u,v>{CC},不管 d i s [ u ] ⊕ d i s [ v ] dis[u] \oplus dis[v] dis[u]dis[v] 的第 k k k 位是什么,都恰能保证有 2 s i z e − 1 2^{size-1} 2size1 条路径的第 k k k 位是 1 1 1
    • 又由于这一位对答案总数的贡献显然是 2 k 2^k 2k,所以某一对 &lt; u , v &gt; &lt;u,v&gt; <uv> 的所有路径的贡献就是 2 s i z e − 1 × 2 k 2^{size-1} \times 2^k 2size1×2k
    • 所以这一整个连通分量对答案总数的贡献就是 C n 2 × ( 2 s i z e − 1 × 2 k ) = = n ( n − 1 ) × 2 s i z e − 1 × 2 k C_{n}^{2} \times (2^{size-1} \times 2^k) == n(n-1)\times 2^{size-1} \times 2^k Cn2×(2size1×2k)==n(n1)×2size1×2k

  • 2. 如果这一位不能通过线性基表示出来,那说明对于某一对 &lt; u , v &gt; &lt;u,v&gt; <uv>,如果要对最终答案产生 2 k 2^k 2k 这么多贡献,所选的 &lt; u , v &gt; &lt;u,v&gt; <uv> 必须使得 d i s [ u ] ⊕ d i s [ v ] dis[u] \oplus dis[v] dis[u]dis[v] 的第 k k k 位是 1 1 1
    • &lt; u , v &gt; &lt;u,v&gt; <uv> 之间异或和不同的路径总条数是 2 s i z e 2^{size} 2size(随便走环都不影响第 k k k 位),所以这一对点对对答案的贡献总数就是 2 s i z e × 2 k 2^{size} \times 2^k 2size×2k
    • 记这个连通分量中第 k k k 位为 1 1 1 d i s [ i ] dis[i] dis[i] n 1 n_1 n1 个,那么符合要求的点对就共有 n 1 ( n − n 1 ) n_1(n-n_1) n1(nn1) 对(选的 &lt; u , v &gt; &lt;u,v&gt; <uv> 须满足 d i s [ u ] dis[u] dis[u] k k k 位为 1 1 1 d i s [ v ] dis[v] dis[v] k k k 位为 0 0 0)所以整个连通分量对答案的贡献是 n 1 ( n − n 1 ) × ( 2 s i z e × 2 k ) n_1(n-n_1) \times (2^{size} \times 2^k) n1(nn1)×(2size×2k)

因此我们对每个分量求出各自的总贡献,再求和即可得解。多次 DFS 就可以轻松解决这个问题。



时间复杂度
  • 动态插入线性基, O (   N log ⁡ ( max ⁡ { w i } )   + M   ) O(\ N \log(\max\{w_i\})\ +M\ ) O( Nlog(max{wi}) +M ) 的。
  • 求第 k k k 个连通分量每一位的贡献, O (   n k log ⁡ ( max ⁡ { w i } )   ) O(\ n_k \log(\max\{w_i\})\ ) O( nklog(max{wi}) ) 或者 O (   log ⁡ 2 ( max ⁡ { d i } )   ) O(\ \log^2(\max\{d_i\})\ ) O( log2(max{di}) ) 的。
  • 求所有连通分量的每一位的贡献, O (   N log ⁡ ( max ⁡ { w i } )   ) O(\ N \log(\max\{w_i\})\ ) O( Nlog(max{wi}) )
  • 总时间复杂度: O (   N log ⁡ ( max ⁡ { w i } )   + M   ) O(\ N \log(\max\{w_i\})\ +M\ ) O( Nlog(max{wi}) +M )




AC代码

【CGWR】毒瘤!累乘取模的时候要格外小心!特别是像对1e9+7这种取模的!!

最好是:

  • 1.每个因子都要 先取模再打括号 ,再用乘号连接每个被括号包住的因子!
  • 2.从左往右,每有俩因子相乘了就 马上取模,再打括号,然后在右括号后面再加乘号进而再乘下一项。一定要记住是 取模了再乘!!取模了再乘!!取模了再乘!!
/*
 *      If we give,
 *      all we've got,
 *      we will make it through.
 */


#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <string>
#include <algorithm>
#include <utility>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <cctype>
#include <climits>

#define GC getchar()
#define _SN(x) {char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define _SAN(a,n) {auto _i=0,_n=n;for(;_i<_n;++_i)_SN(a[_i])}
#define _SA(a,l,r) {auto _i=l,_r=r;for(;_i<_r;++_i)_SN(a[_i])}
#define _gS(_1, _2, _3, _sc, ...) _sc
#define sc(...) _gS(__VA_ARGS__,_SA,_SAN,_SN, ...)(__VA_ARGS__)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define _F0N(i,n) for(auto i=0;i<n;++i)
#define _FLR(i,l,r) for(auto i=l,_r=r;i<_r;++i)
#define _gF(_1, _2, _3, _F, ...) _F
#define F(...) _gF(__VA_ARGS__,_FLR,_F0N, ...)(__VA_ARGS__)
#define _FD0(i,n) for(auto i=0;i<=n;++i)
#define _FDL(i,l,r) for(auto i=l,_r=r;i<=_r;++i)
#define _gFD(_1, _2, _3, _FD, ...) _FD
#define FD(...) _gFD(__VA_ARGS__,_FDL,_FD0, ...)(__VA_ARGS__)

#define OPER1(T,x1,b1) inline bool operator<(const T&_o)const{return x1 b1 _o.x1;}
#define OPER2(T,x1,b1,x2,b2) inline bool operator<(const T&_o)const{return x1 b1 _o.x1||x1==_o.x1&&x2 b2 _o.x2;}
#define OPER3(T,x1,b1,x2,b2,x3,b3) inline bool operator<(const T&_o)const{return x1 b1 _o.x1||x1==_o.x1&&(x2 b2 _o.x2||x2==_o.x2&&x3 b3 _o.x3);}

#define LL long long
#define ULL unsigned LL
#define PC putchar
template<class T>
void PRT(const T _){if(_<0){PC(45),PRT(-_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}

#define CON constexpr
#define T_CASE int T;sc(T)for(int CASE=1;CASE<=T;++CASE)
#define Tjj int T;sc(T)while(T--)
#define qjj int q;sc(q)while(q--)
#define cincout std::cin.tie(nullptr),std::cout.tie(nullptr),std::ios::sync_with_stdio(false);
#define EPS 1e-8
#define PI 3.141592653589793
#define MAX_INT 2147483647
#define MIN_INT -2147483648
#define MAX_LL 9223372036854775807LL
#define MIN_LL -9223372036854775808LL
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3fLL
#define endl '\n'
#define priority_queue priority_queue
#define PQ std::priority_queue
#define PR std::pair
#define vector vector
#define unordered_ unordered_
#define VI std::vector<int>
#define MII std::map<int,int>
#define MLI std::map<LL,int>
#define MSI std::map<std::string,int>
#define PII std::pair<int,int>
#define PLI std::pair<LL,int>
#define PSI std::pair<std::string,int>
#define MPFD(k) auto it=mp.find(k)

#define MIN(a, b) ((a)<(b)?(a):(b))
#define MIN3(a, b, c) (MIN(a, MIN(b, c)))
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MAX3(a, b, c) (MAX(a, MAX(b, c)))
#define ABS(a) ((a)>0?(a):-(a))
#define FABS(a) ((a)>0?(a):-(a))
#define log2n(x) (log(x)/0.69314718055995)
#define MHD(p1, p2) ((p1.x>p2.x?p1.x-p2.x:p2.x-p1.x)+(p1.y>p2.y?p1.y-p2.y:p2.y-p1.y))

#define PB emplace_back
#define EB emplace_back
#define BRK else break
#define ALL(X) (X).begin(),(X).end()
#define SORT(X) std::sort(ALL(X))
#define SORTD(X) std::sort(ALL(X),std::greater<decltype((X)[0])>())
#define SWAP(a, b) do{auto _t=a; a=b; b=_t;}while(0)
#define mem0(a) memset(a,0,sizeof(a))
#define memf1(a) memset(a,-1,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))



CON int MV(1e5+7), ME(2e5+7);
CON ULL MOD(1000000007ull);

struct Ed
{
	int next, v;
	ULL d;
} ed[ME * 2];
int tot, head[MV];
#define edd(uu, vv, dd) ed[++tot].next=head[uu], ed[tot].d=dd, ed[tot].v=vv, head[uu]=tot


#define ONE 1ull
constexpr int MB(61);
namespace XB
{
ULL b[66];
int size;
inline void clear(void)
{
	size = 0,
	memset(b, 0, sizeof(b));
}

bool has(const ULL MASK)
{
	for (int i=0; i<=MB; ++i)
		if (b[i] & MASK)
			return true;
	return false;
}

bool insert(ULL v)
{
	for (int i=MB; i>=0; --i)
	{
		if (v & (ONE << i))
		{
			if (b[i])
				v ^= b[i];
			else
			{
				b[i] = v, ++size;
				break;
			}
		}
	}

	return v;
}
}


bool vis[MV];
ULL dis[MV];
int sta[MV], top;

void dfs(const int u)
{
	vis[u] = true, sta[top++] = u;

	for (int i=head[u]; i; i=ed[i].next)
	{
		const int v = ed[i].v;
		if (vis[v])
			XB::insert(dis[u] ^ dis[v] ^ ed[i].d);
		else
			dis[v] = dis[u] ^ ed[i].d, dfs(v);
	}
}


int main()
{
	ULL d;
	get(V, E)
	while (E--)
	{
		get(u, v)
		sc(d)
		edd(u, v, d);
		edd(v, u, d);
	}

	int cnt[2];
	ULL ans = 0, mask;
	FD(v, 1, V)
	{
		if (!vis[v])	// one connected-component
		{
			XB::clear();
			top = 0, dfs(v);

			for (int k=0; k<=MB; ++k)
			{
				mask = ONE << k;
				if (XB::has(mask))
				{
					ans += (((mask % MOD) * ((ONE << (XB::size-1)) % MOD)) % MOD * (((ULL)top * (top-1) / 2) % MOD)) % MOD;
					if (ans >= MOD)
						ans -= MOD;
				}
				else
				{
					cnt[0] = cnt[1] = 0;
					for (int i=0; i<top; ++i)
						++cnt[!!(dis[sta[i]] & mask)];
					ans += (((mask % MOD) * ((ONE << XB::size) % MOD)) % MOD * (((ULL)cnt[0] * cnt[1]) % MOD)) % MOD;
					if (ans >= MOD)
						ans -= MOD;
				}
			}
		}
	}

	UPRT(ans);

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值