Problem
Solution
这道题目思路会比较像wc2011的xor,可能会更难一点,是一道线性基的好题
你得知道维护对角矩阵的线性基保证对于所有线性基,二进制任意一位最多只有一个为1。但由于构造对角矩阵的线性基时间复杂度为 ,所以我们一般(包括下文中的代码)写的是维护上三角矩阵的线性基。如果这个线性基第i位至少有一个1,那么显然前者也会有。
首先可以用一个O(n)的dfs处理出所有的环的异或和,我们只需记录一个异或和即可。更形象地说,dfs就相当于搜成一棵树,开始的节点就是根节点,一旦碰到已经在树上的点,由于异或的性质,dis[x] xor w xor dis[y]就相当于这个环的异或和,当然了可能没计入的大环是可以由小环组合而来的。由于图不保证联通,那么分联通块计算。
我们先枚举这两个点,容易知道dis[x] xor dis[y]就是两点路径的异或和
按位来考虑贡献,设当前处理的是第i位,要有贡献,我们需要分情况讨论。
不妨记线性基有tot个,dis[x]第i位为0的个数有cnt[0]个,对cnt[1]也是同样的定义
首先如果两点路径的异或和第i位为0的话,那么第i位做贡献,必须异或一些环使得最后第i位为1。选中这一位的线性基,其他的任意组合。那么贡献为
另一种情况,我们在线性基中找有没有第i位为1的,如果没有,那么就可以tot个任意组合,如果有,那么去掉它再任意组合
Code
关于调试的一个小trick,编译的时候加-ftrapv命令,如果re就说明爆int/long long了
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=100010,mod=1000000007;
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
struct data{int v,nxt;ll w;}edge[maxn<<2];
int n,m,p,sz,fac[70],q[maxn],head[maxn];
ll ans,dis[maxn],l[70];
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline void insert(int u,int v,ll w)
{
edge[++p]=(data){v,head[u],w};head[u]=p;
edge[++p]=(data){u,head[v],w};head[v]=p;
}
void input()
{
int u,v;ll w;
read(n);read(m);
for(int i=1;i<=m;i++)
{
read(u);read(v);read(w);
insert(u,v,w);
}
}
void add(ll x)
{
for(int i=63;~i;i--)
if(x&(1ll<<i))
{
if(!l[i]){l[i]=x;return ;}
x^=l[i];
}
}
void dfs(int x,int pre)
{
q[++sz]=x;
for(int i=head[x];i;i=edge[i].nxt)
if(edge[i].v!=pre)
{
if(~dis[edge[i].v]) add(dis[x]^edge[i].w^dis[edge[i].v]);
else dis[edge[i].v]=dis[x]^edge[i].w,dfs(edge[i].v,x);
}
}
void calc()
{
int f,tot=0,cnt[2];
ll now;
for(int i=0;i<=63;i++) if(l[i]) tot++;
for(int i=0;i<=63;i++)
{
cnt[0]=cnt[1]=f=0;
for(int j=1;j<=sz;j++) cnt[(dis[q[j]]>>i)&1]++;
for(int j=0;j<=63&&!f;j++) if(l[j]&(1ll<<i)) f=1;
now=((ll)cnt[0]*(cnt[0]-1)/2+(ll)cnt[1]*(cnt[1]-1)/2)%mod;
if(f)
{
if(tot) now=now*fac[tot-1]%mod;
now=now*fac[i]%mod;ans=pls(ans,now);
}
now=(ll)cnt[0]*cnt[1];
if(f) {if(tot) now=now*fac[tot-1]%mod;}
else now=now*fac[tot]%mod;
now=now*fac[i]%mod;ans=pls(ans,now);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
input();fac[0]=1;
memset(dis,0xff,sizeof(dis));
for(int i=1;i<70;i++) fac[i]=pls(fac[i-1],fac[i-1]);
for(int i=1;i<=n;i++)
if(dis[i]==-1)
{
memset(l,0,sizeof(l));
sz=dis[i]=0;dfs(i,0);calc();
}
printf("%lld\n",ans);
return 0;
}