Xor-matic Number of the Graph-CodeForces - 724G

Xor-matic Number of the Graph-CodeForces - 724G

线性基棒题

建议做这题前先看看线性基的概念,然后A掉这道题--->路径最大异或和

这两个题都用到了一个相同的性质:

任何一条路径的异或值都可以随意地与任意多个环相接

对于这道题来说,每一条路径都有它独立的贡献,并且都需要和每一个异或和不同的环相接

处理环异或和不同自然使用线性基判断插入是否成功

所以是一个全局统计的题目

如何统计?

将所有点到根节点的异或前缀和存下来,相互之间异或,就得到了所有路径的异或值,然后再添加环就很简单了

如何存呢?当然是按位算贡献

详见代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define reg register
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
inline int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}


const int N=5e5+10,E=2e5+10,P=1e9+7;

int n,m;

struct Edge{
    int to,nxt;
    ll w;
}e[E<<1];
int head[N],ecnt;
void AddEdge(int u,int v,ll w){
    e[++ecnt]=(Edge){v,head[u],w};
    head[u]=ecnt;
}
#define erep(u,i) for(int i=head[u];i;i=e[i].nxt)



ll dis[N];
int vis[N];
int cnt[70][2];
ll tmp[70][2];
vector <int> Points;
vector <ll> cir;
void dfs(int u){
    Points.push_back(u);
    rep(i,0,60) if(dis[u]&(1ll<<i)) cnt[i][1]++;
    else cnt[i][0]++;
    vis[u]=1;
    erep(u,i) {
        int v=e[i].to;
        if(vis[v]) {
            cir.push_back(dis[v]^dis[u]^e[i].w);//说明这条边在一个环上
            continue;
        }
        dis[v]=dis[u]^e[i].w;
        dfs(v);
    }
}


ll Ans;
ll d[70];
bool Ins(ll d[],ll x){
    drep(i,60,0) if(x&(1ll<<i)) {
        if(d[i]) x^=d[i];
        else {
            d[i]=x;
            return true;
        }
    }
    return false;
}//线性基
void Solve(int rt){
    Points.clear();cir.clear();
    memset(cnt,0,sizeof cnt);memset(d,0,sizeof d); memset(tmp,0,sizeof tmp);
    dfs(rt);
    rep(k,0,(int)Points.size()-1) {
        ll t=dis[Points[k]];
        rep(i,0,60) if(t&(1ll<<i)) cnt[i][1]--;
        else cnt[i][0]--;
        rep(i,0,60) {
            if(t&(1ll<<i)) tmp[i][0]+=cnt[i][1],tmp[i][1]+=cnt[i][0];
            else tmp[i][0]+=cnt[i][0],tmp[i][1]+=cnt[i][1];
            tmp[i][0]%=P,tmp[i][1]%=P;//依次与其他点形成贡献
        }
        rep(i,0,60) if(t&(1ll<<i)) cnt[i][1]++;
        else cnt[i][0]++;
    }
    rep(i,0,(int)cir.size()-1) {//加入环
        ll t=cir[i];
        if(!Ins(d,t)) continue;
        rep(i,0,60) {
            if(t&(1ll<<i)) {
                ll t=(tmp[i][0]+tmp[i][1])%P;
                tmp[i][0]=tmp[i][1]=t;
            } else tmp[i][0]*=2,tmp[i][1]*=2;
            tmp[i][0]%=P,tmp[i][1]%=P;
        }
    }
    rep(i,0,60) {
        ll b=(1ll<<i)%P;
        (Ans+=tmp[i][1]%P*b%P)%=P;
    }
}
int main() {
    n=rd(),m=rd();
    rep(i,1,m) {
        int u=rd(),v=rd();
        ll w; scanf("%lld",&w);
        AddEdge(u,v,w);
        AddEdge(v,u,w);
    }
    rep(i,1,n) if(!vis[i]) Solve(i);//图不保证联通
    printf("%lld\n",Ans*500000004%P);//答案会被多算一次,所以乘上Inv(2,1e9+7)
}

转载于:https://www.cnblogs.com/chasedeath/p/11285489.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值