codeforces 724G - Xor-matic Number of the Graph 线性基+图

题目传送门

题意:给出衣服无向带权图,问有多少对合法的$<u,v,s>$,要求$u$到$v$存在一条路径(不一定是简单路径)权值异或和等于$s$,并且$u<v$。求所有合法三元组的s的和。

思路:

  参考了一篇大佬的博客。

  这类题的核心思想就是,两点之间的所有可能的路径,都是由一条简单路径加上若干个环组成的。u,v两点所有路径的异或值的集合,等价于,u,v一条简单路径的异或值,与整个连通图的所有环组成的线性基异或的集合。

  所以按位考虑每个二进制1给整幅图带来的价值。

  特别要注意的一点是,在处理线性基的过程中,最多处理到63位,如果处理到64位及以上,当$i>=64$,某些$(x>>i)$,或者$(x<<i)$,就会发出许多诡异的错误,wa了很久。

#pragma GCC optimize (2)
#pragma G++ optimize (2)
#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#include<cstdio>
#include<vector>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,b,a) for(int i=b;i>=a;i--)
#define clr(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pii pair<int,int >
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
ll rd()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=100010;
const ll mod=1e9+7;
int n,m;
struct edge{
    int to;
    ll w;
};
vector<edge >ve[maxn];
int vis[maxn];
ll del[maxn],p[70],siz,num[70],ans=0,tot=0;
void init(){
    rep(i,1,n){
        ve[i].clear();
        vis[i]=0;
    }
    ans=0,siz=0,clr(p,0);
}
bool insert(ll x){
    dep(i,63,0){
        if((x>>i)&1){
            if(!p[i]){
                p[i]=x;
                return true;
            }
            x^=p[i];
        }
    }
    return false;
}
void dfs(int u,ll res){
    del[u]=res;
    for(ll i=63;i>=0;i--){
        if(((res>>i)&1)){
            num[i]++;
        }
    }
    tot++;
    vis[u]=1;
    for(auto &st:ve[u]){
        if(!vis[st.to]){
            dfs(st.to,res^st.w);
        }else{
            if(insert(res^st.w^del[st.to])){
                siz++;
            }
        }
    }
}
int main(){
    while(cin>>n>>m){
        init();
        rep(i,1,m){
            int u,v;
            ll w;
            u=rd(),v=rd(),w=rd();
            ve[u].pb({v,w});
            ve[v].pb({u,w});
        }
        rep(u,1,n){
            if(!vis[u]){
                tot=0;
                clr(p,0),siz=0;
                clr(num,0);
                dfs(u,0);
                dep(i,63,0){
                    bool ok=0;
                    dep(j,63,0){
                        if((p[j]>>i)&1)ok=1;
                    }
                    if(ok){
                        ans=(ans+tot*(tot-1)/2%mod*((1ll<<(siz-1))%mod)%mod*((1ll<<i)%mod)%mod)%mod;
                    }else{
                        ans=(ans+num[i]*(tot-num[i])%mod*((1ll<<siz)%mod)%mod*((1ll<<i)%mod)%mod)%mod;
                    }
                }
            }
        }
        printf("%lld\n",ans);
    }
}

 

 

posted @ 2019-10-08 14:26  光芒万丈小太阳  阅读(222)  评论(0编辑  收藏  举报