codeforces724G - Xor-matic Number of the Graph

20 篇文章 0 订阅
1 篇文章 0 订阅

题意:

一个无向图,边上有边权。定义一条路的价值为这条路径上边权的异或和,两个点之间的价值为它们所有路径不同价值的和,整个图的价值为所有点对价值的和。
求整个图的价值。

做法:

首先考虑对于树的做法:

记点i到根路径的异或和为f[i],则点对u,v路径的异或和就是f[u]^f[v].
n^2做法就直接枚举任意两点。
考虑“异或”的套路:由于每一位互不干扰,我们可以按位处理。
对于第x位,如果f[1..n]中有a个1,b个0,对答案的贡献就是a×b×2^x.

发现对于一个图的一个连通分量,其实就是它的一个生成树上有一些非树边,构成了环。
假如跑出任意一个生成树,任意两点的价值就是它树上的路径的异或和异或上一些简单环的异或和。
树上的问题就解决了,还要处理环的问题。
我们可以把所有环的异或和放到一个线性基里去。
然后类似的,按位处理出每一位对答案的贡献。
对于第x位,如果f[1..n]中有a个1,b个0,则他们有a×b种方案得到1.

记线性基中有m个元素。
如果这一位线性基中的元素全为0,则随便怎么选都可以,共2^m种方案。
如果这一位线性基中有某个元素为1,则无论其它元素怎么选,这个元素都存在唯一的选或不选的方案,共2^(m-1)种方案。

当然,他们还有C(a,2)+C(b,2)种方案得到0.

如果这一位线性基中的元素全为0,则不可能得到1,方案数为0。
如果这一位线性基中有某个元素为1,则无论其它元素怎么选,这个元素都存在唯一的选或不选的方案,共2^(m-1)种方案(同上)。

然后用乘法原理和加法原理就可以计算出答案(注意别忘记乘这一位的值2^x)
记得开long long.

代码:

/*************************************************************
    Problem: codeforces 724G - Xor-matic Number of the Graph
    User: RainbowGirl
    Language: C++
    Result: Accepted
    Time: 233 ms
    Memory: 13100 KB
    Submit_Time: 2018-01-15 10:08:58
*************************************************************/

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cctype>
using namespace std;
typedef long long LL;

inline LL read()
{
    char ch = getchar(); LL x = 0; int op = 1;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
    for(; isdigit(ch); ch = getchar()) x = x*10 + ch-'0';
    return x*op;
}
inline void write(LL a) { if(a < 0) putchar('-'), a = -a; if(a >= 10) write(a/10); putchar(a%10+'0'); }

const int N = 200010;
const LL mod = 1e9 + 7;
int n, m, cnt;
int head[N], vis[N];
LL f[N], g[100][2], a[100], p[200], used, ans, tot;
struct edge{
    int to, nxt; LL v;
    edge() {}
    edge(int x, int y, LL z) { to = x, nxt = y, v = z; }
}e[N<<1];

inline void addedge(int x, int y, LL z) { e[++ cnt] = edge(y, head[x], z); head[x] = cnt; }
inline void insert(LL x)
{
    used |= x;
    for(int i = 60; i >= 0; i --)
        if(x>>i&1) {
            if(a[i]) x ^= a[i]; else { tot ++; a[i] = x; break; }
        }
}
inline void inc(LL x) { for(int i = 0; i < 61; i ++) g[i][x>>i&1] ++; }//f[i]的每一位0/1的个数
inline void dfs(int u, int lst)
{
    vis[u] = 1; inc(f[u]);
    for(int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to; if(v == lst) continue;
        if(!vis[v]) {
            f[v] = f[u]^e[i].v;
            dfs(v, u);
        } else insert(f[v]^f[u]^e[i].v);//把环的异或值插入线性基
    }
}
inline void cal()
{
    LL tmp;
    for(int i = 60; i >= 0; i --)
        if(used>>i&1) {
            tmp = g[i][0]*(g[i][0]-1)/2%mod;
            tmp = (tmp+g[i][1]*(g[i][1]-1)/2%mod)%mod;
            tmp = (tmp+g[i][1]*g[i][0]%mod)%mod;
            ans = (ans+p[tot-1]*p[i]%mod*tmp%mod)%mod;
        } else {
            tmp = g[i][0]*g[i][1]%mod;
            ans = (ans+p[tot]*p[i]%mod*tmp%mod)%mod;
        }
    memset(g, 0, sizeof g); memset(a, 0, sizeof a); used = 0; tot = 0;
}
int main()
{
    n = read(), m = read();
    p[0] = 1;
    for(int i = 1; i <= 150; i ++) p[i] = 2LL*p[i-1]%mod;
    for(int i = 1; i <= m; i ++) {
        int x = read(), y = read(); LL z = read();
        addedge(x, y, z); addedge(y, x, z);
    }
    for(int i = 1; i <= n; i ++)
        if(!vis[i]) { dfs(i, 0); cal(); }
    write(ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值