CF724G Xor-matic Number of the Graph

给你一个无向图,有n个顶点和m条边,每条边上都有一个非负权值。

我们称一个三元组 \((u,v,s)\) 是有趣的,当且仅当对于 \(u,v\in [1,n]\) ,有一条从 \(u\)\(v\) 的路径(可以经过相同的点和边多次),其路径上的权值异或和为 \(s\) 。对于一条路径,如果一条边经过了多次,则计算异或和时也应计算多次。不难证明,这样的三元组是有限的。

计算所有有趣的三元组中 \(s\) 的和对于 \(10^9+7\) 的模数

首先考虑如何计算答案,枚举每一条简单路径(可以写成\(dis_u\oplus dis_v\)\(dis\)表示到\(1\)号点的异或和),然后和所有其他的环都能作为答案。

而这样子并不容易做,我们注意到线性基的一个性质。

假设该线性基的元素个数为\(cnt\)个,那么该线性基可以表示\(2^{cnt}\)个不同的数,而如果线性基的第\(x\)位为\(1\),那么有\(2^{cnt-1}\)个数的第\(x\)\(1\)

这启发我们去对每一位分开考虑计算贡献。

如果线性基第\(x\)位为\(1\),那么不管\(u,v\)怎么选都能使异或和第\(x\)位为\(1\),那么贡献为\(2^x2^{cnt-1}\begin{pmatrix}n\\2\end{pmatrix}\)

如果线性基第\(x\)位不为\(1\),那么找到\(dis_u\)的第\(x\)位的个数\(s\),那么需要选一个第\(x\)\(1\)的端点和一个第\(x\)不为\(1\)的端点,而线性基上的\(2^{cnt}\)个数可以随便选,贡献为\(2^x2^{cnt}s(n-s)\)

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 2e5;
const int P = 1e9 + 7;
using namespace std;
struct edges
{
    int to;
    long long cost;
}edge[N * 2 + 5];
int n,m,head[N + 5],nxt[N * 2 + 5],edge_cnt,vis[N + 5],cnt,ans,nn,sm[100];
long long dis[N + 5],p[100];
void add_edge(int u,int v,long long w)
{
    edge[++edge_cnt] = (edges){v,w};
    nxt[edge_cnt] = head[u];
    head[u] = edge_cnt;
}
void ins(long long x)
{
    for (int i = 62;i >= 0;i--)
        if (x >> i & 1)
        {
            if (!p[i])
            {
                p[i] = x;
                cnt++;
                break;
            }
            x ^= p[i];
        }
}
void dfs(int u,long long cnt)
{
    vis[u] = 1;
    nn++;
    dis[u] = cnt;
    for (int i = 62;i >= 0;i--)
        if (dis[u] >> i & 1)
            sm[i]++;
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i].to;
        long long w = edge[i].cost;
        if (!vis[v])
            dfs(v,cnt ^ w);
        else
            ins(dis[u] ^ dis[v] ^ w);
    }
}
void calc(int st)
{
    memset(p,0,sizeof(p));
    memset(sm,0,sizeof(sm));
    cnt = 0;
    nn = 0;
    dfs(st,0);
    for (int i = 62;i >= 0;i--)
    {
        int fl = 0;
        for (int j = 62;j >= 0;j--)
            if (p[j] >> i & 1)
            {
                fl = 1;
                break;
            }
        if (fl)
            ans += 1ll * nn * (nn - 1) / 2 % P * ((1ll << i) % P) % P * ((1ll << cnt - 1) % P) % P,ans %= P;
        else
            ans += (1ll << cnt) % P * ((1ll << i) % P) % P * sm[i] % P * (nn - sm[i]) % P,ans %= P;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    int u,v;
    long long w;
    for (int i = 1;i <= m;i++)
    {
        scanf("%d%d%lld",&u,&v,&w);
        add_edge(u,v,w);
        add_edge(v,u,w);
    }
    for (int i = 1;i <= n;i++)
        if (!vis[i])
            calc(i);
    cout<<ans<<endl;
    return 0;
}
posted @ 2020-09-15 11:28  eee_hoho  阅读(134)  评论(0编辑  收藏  举报