[CF724G]Xor-matic Number of the Graph
题目
题解
有了这道题的思路,很简单便知道,我们可以先找到一条简单路径,然后用图中的环对这条路径进行拓展。
显然,我们先用图中所有的环(除去两点环)做一个线性基,然后考虑怎么做题。
但是由于这道题是针对所有的数,所以我们可以随便建一棵生成树,得到每个点到根的路径异或和 \(s[u]\),对于两个点 \(u,v\) 他们的简单路径异或和即为 \(s[u]\oplus s[v]\),现在我们所要知道的就是,这两点的异或和可能会有多少个不同的值。
考虑异或和二进制中的每一位 \(i\) 的分别有 \(0,1\) 两种情况,只有当这一位为 \(1\) 才会对最后的 \(\sum s\) 做出贡献,现在讨论怎么样才能使路径异或和为 \(1\)。
为了方便表示,我们设 \(x[i]\) 表示数 \(x\) 在二进制下的第 \(i\) 位。
把所有点的第 \(i\) 位的 \(0,1\) 情况分别用 \(c[0],c[1]\) 来统计,前者为 \(s[u][i]=0\) 的 \(u\) 的个数,后者同理。
- \(s[u][i]=1,s[v][i]=0\),显然可以得到 \((s[u]\oplus s[v])[i]=1\),这种情况出现的次数有 \(c[1]\times c[0]\);
- \(s[u][i]=1,s[v][i]=1\),显然可以得到 \((s[u]\oplus s[v])[i]=0\),这种情况出现的次数为 \(c[1]\times (c[1]-1)\);
- \(s[u][i]=0,s[v][i]=0\),显然可以得到 \((s[u]\oplus s[v])[i]=0\),这种情况出现的次数为 \(c[0]\times (c[0]-1)\);
由于 \(u,v\) 地位相同,我们就不讨论 \(s[u][i]=0,s[v][i]=1\) 的情况了(反而会重复计算)。
但是发现,我们之前的分类讨论中,出现路径异或和 \(i\) 位为 \(0\) 的情况,是不是没有贡献呢?错,因为我们还没有考虑到后面环的拓展对路径做出的贡献,还要对线性基进行讨论。
说明:\(siz\) 表示线性基大小。
- 线性基中不存在 \(i\) 位为 \(1\),这种情况下,线性基对于前面第一种情况的贡献为 \(2^{siz}\),对于情况 \(2,3\) 的贡献为 \(0\);
- 线性基中存在 \(i\) 位为 \(1\),这种情况下,线性基对于第 \(2,3\) 的贡献为 \(2^{siz-1}\),对于第一种情况的贡献为 \(2^{siz-1}\)(选此位不为 \(1\) 的组合);
最后,我们要记得乘上 \(2^i\) 表示这一位为 \(1\) 的贡献。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,__l,__r) for(int i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(int i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define uint unsigned int
#define pii pair< int,int >
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long
#define cg (c=getchar())
template<class T>inline void qread(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T qread(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
#undef cg
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int MAXN=1e5;
const int MAXM=2e5;
const int JZM=1e9+7;
const int inv2=500000004;
template<class T>struct Basic{
#define MAXSIZE 62
T f[MAXSIZE+5],g[MAXSIZE+5];
int flg,siz;
inline void clear(){
memset(f,0,sizeof f);flg=siz=0;
}
inline void insert(T num){
fep(i,MAXSIZE,0){
if(!(num>>i))continue;
if(!f[i]){f[i]=num;++siz;return;}
num^=f[i];
}
++flg;
}
inline T queryMax(T ret){
fep(i,MAXSIZE,0)if((ret^f[i])>ret)
ret^=f[i];
return ret;
}
inline T queryMin(){
if(flg)return 0;
T ret=(1ll<<MAXSIZE);
rep(i,0,MAXSIZE)ret=Min(ret,f[i]);
}
inline void buildg(){
rep(i,0,MAXSIZE)g[i]=f[i];
rep(i,0,MAXSIZE)rep(j,0,i-1)if(g[i]&(1ll<<j))
g[i]^=g[j];
}
inline T queryNum(T k){
//must build g before use it
if(flg)--k;
if(k==0)return 0;
T ret=0;
rep(i,0,MAXSIZE)if(g[i]){
if(k&1)ret^=g[i];
k>>=1;
}
return k?-1:ret;
}
};
Basic<LL>G;
struct edge{int to,nxt;LL w;}e[(MAXM<<1)+5];
int tail[MAXN+5],ecnt=-1;
inline void add_edge(const int u,const int v,const LL w){
e[++ecnt]=edge{v,tail[u],w};tail[u]=ecnt;
e[++ecnt]=edge{u,tail[v],w};tail[v]=ecnt;
}
int n,m;
LL ans;
inline void Init(){
memset(tail,-1,sizeof tail);
cin>>n>>m;
int u,v;LL w;
rep(i,1,m){
cin>>u>>v>>w;
add_edge(u,v,w);
}
}
bool vis[MAXN+5];
LL s[MAXN+5],q[MAXN+5];
int qcnt;
void dfs(const int u,const int pre){
vis[u]=true,q[++qcnt]=s[u];
for(int i=tail[u],v;~i;i=e[i].nxt){
v=e[i].to;
if(pre==(i^1))continue;
//排除走反边形成两点环
if(vis[v]){
G.insert(s[u]^s[v]^e[i].w);
continue;
}
s[v]=s[u]^e[i].w;
dfs(v,i);
}
}
inline LL qkpow(LL a,int n){
LL ret=1;
for(;n>0;n>>=1){
if(n&1)ret=ret*a%JZM;
a=a*a%JZM;
}
return ret;
}
inline LL query(const int i,const int num){
int flg=0;
rep(j,0,MAXSIZE)if((G.f[j]>>i)&1){
flg=1;break;
}
if(num==1){
if(flg)return qkpow(2ll,G.siz-1);
return 0;
}else{
if(flg)return qkpow(2ll,G.siz-1);
return qkpow(2ll,G.siz);
}
}
LL c[2];
void calc(){
rep(i,0,MAXSIZE){
c[0]=c[1]=0;
rep(j,1,qcnt)++c[(q[j]>>i)&1];
if(c[0])ans=(ans+(1ll<<i)%JZM*c[0]%JZM*(c[0]-1)%JZM*inv2%JZM*query(i,1)%JZM)%JZM;
if(c[1])ans=(ans+(1ll<<i)%JZM*c[1]%JZM*(c[1]-1)%JZM*inv2%JZM*query(i,1)%JZM)%JZM;
ans=(ans+(1ll<<i)%JZM*c[0]%JZM*c[1]%JZM*query(i,0)%JZM)%JZM;
}
}
signed main(){
Init();
rep(i,1,n)if(!vis[i]){
G.clear(),qcnt=0;
dfs(i,-1);
calc();
}
cout<<ans<<'\n';
return 0;
}