1. 程式人生 > 實用技巧 >CF724G Xor-matic Number of the Graph

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;
}