与
bzoj2115
类似,点
x
到点
对于每个联通块,先考虑是一棵树的情况。求出每个点
i
到根的路径的异或和
当有环时,先求出每个环的异或和,加入线性基中。求出图的
dfs
树和
ai,bi
。
考虑二进制的每一位对答案的贡献。令
k
表示线性基中数的个数。
对于第
- 如果线性基中每个数这一位的值都是
0
,那么线性基中每个数可以自由组合,对答案的贡献就是
2i+kaibi 。 - 如果线性基中这一位的值可以是
1
,那么我们可以取出这个数,其他数自由组合,然后判断是否要加入这个数,对答案的贡献就是
2i+k−1(aibi+ai(ai−1)2+bi(bi−1)2) 。
时间复杂度 O(nlogtmax)
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100010
#define M 63
#define P 1000000007
struct Edge{
ll w;
int t,nx;
}e[N<<3];
ll a[M],d[N],Ans,z,s[2],p[M<<1];
int i,j,k,n,m,x,y,Num,h[N],H[N];
bool b[N];
inline void Add(int x,int y,ll s){
e[++Num].t=y;e[Num].w=s;e[Num].nx=h[x];h[x]=Num;
}
inline void Add2(int x,int y,ll s){
e[++Num].t=y;e[Num].w=s;e[Num].nx=H[x];H[x]=Num;
}
inline void Ins(ll x){
for(int i=M-1;i;i--)
if((x>>i-1)&1)
if(!a[i]){
a[i]=x;
break;
}else x^=a[i];
}
inline bool Check(int x){
for(int i=1;i<M;i++)
if((a[i]>>x-1)&1)return 1;
return 0;
}
inline void Dfs(int x,int y,ll s){
if(b[x]){
Ins(s^d[x]);
return;
}
b[x]=1;d[x]=s;
for(int i=h[x];i;i=e[i].nx)
if(e[i].t==y)Add2(y,x,e[i].w);else Dfs(e[i].t,x,s^e[i].w);
}
inline void Dfs2(int x,int y){
for(int i=H[x];i;i=e[i].nx)Dfs2(e[i].t,y);
s[(d[x]>>y)&1]++;
}
inline ll Calc(int x){
ll Ans=0;k=0;
for(int i=1;i<M;i++)if(a[i])k++;
for(int i=1;i<M;i++){
bool d=Check(i);
s[0]=s[1]=0;
Dfs2(x,i-1);
if(d)Ans=(Ans+((s[0]*(s[0]-1)>>1)+(s[1]*(s[1]-1)>>1))%P*p[k+i-2]%P+s[0]*s[1]%P*p[k+i-2])%P;else
Ans=(Ans+s[0]*s[1]%P*p[k+i-1])%P;
}
return Ans;
}
int main(){
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)scanf("%d%d%I64d",&x,&y,&z),Add(x,y,z),Add(y,x,z);
for(p[0]=i=1;i<(M<<1);i++)p[i]=(p[i-1]<<1)%P;
for(i=1;i<=n;i++)
if(!b[i]){
memset(a,0,sizeof(a));
Dfs(i,0,0);
Ans=(Ans+Calc(i))%P;
}
cout<<Ans<<endl;
return 0;
}