zoukankan      html  css  js  c++  java
  • 【CF724G】Xor-matic Number of the Graph

    题目

    题目链接:https://codeforces.com/problemset/problem/724/G
    给你一个无向图,有 (n) 个顶点和 (m) 条边,每条边上都有一个非负权值。
    我们称一个三元组 ((u,v,s)) 是有趣的,当且仅当对于 (u,vin [1,n]) ,有一条从 (u)(v) 的路径(可以经过相同的点和边多次),其路径上的权值异或和为 (s) 。对于一条路径,如果一条边经过了多次,则计算异或和时也应计算多次。不难证明,这样的三元组是有限的。
    计算所有有趣的三元组中 (s) 的和对于 (10^9+7) 的模数。
    (nleq 10^5,mleq 2 imes 10^5, ext{边权}leq 10^{18})

    思路

    把每一个连通块单独拎出来做。
    我们知道,(x)(y) 的任意一条路径异或和可以看作一条 (x)(y) 的简单路径的异或和再异或上若干个环。那么随便找一棵现在处理的连通块的生成树,对于非树边,都可以与一些树边组合形成环。把这些环的异或和扔进线性基里。
    发现每一位是互不影响的,考虑计算每一位的贡献。
    对于第 (i) 位,假设存在一个环的第 (i) 位是 (1),那么对于任意两个点,他们之间一定存在一条路径的第 (i) 位是一。此时线性基中除了第 (i) 位以外所有元素都可以选择,贡献就是

    [inom{n}{2} imes 2^{ ext{siz}-1} imes 2^{i} ]

    其中 ( ext{siz}) 是线性基大小。
    如果不存在任意一个环的第 (i) 位是 (1),我们记 ( ext{dis}_x) 表示该连通块生成树的根到 (x) 路径的异或和,那么只有 ( ext{dis})(0) 的和 ( ext{dis})(1) 的之间才能产生贡献,此时线性基中任意元素都可以选择,贡献为

    [ ext{cnt}_i(n- ext{cnt}_i) imes 2^{ ext{siz}} imes 2^{i} ]

    其中 ( ext{cnt}_i) 表示 ( ext{dis})(i) 位为 (1) 的数量。
    时间复杂度 (O((n+m)log a))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=400010,LG=60,MOD=1e9+7;
    int n,m,tot,head[N],cnt[LG+1];
    ll ans,dis[N];
    bool vis[N];
    
    struct VectorSpace
    {
    	int siz;
    	ll c[LG+1];
    	bool flag[LG+1];
    	
    	void ins(ll x)
    	{
    		if (!x) return;
    		for (int i=0;i<=LG;i++)
    			if (x&(1LL<<i)) flag[i]=1;
    		for (int i=LG;i>=0;i--)
    			if (x&(1LL<<i))
    			{
    				if (!c[i]) { c[i]=x; siz++; break; }
    				x^=c[i];
    			}
    	}
    	
    	void clr()
    	{
    		memset(flag,0,sizeof(flag));
    		memset(c,0,sizeof(c));
    		siz=0;
    	}
    }vs;
    
    struct edge
    {
    	int next,to;
    	ll dis;
    }e[N];
    
    void add(int from,int to,ll dis)
    {
    	e[++tot]=(edge){head[from],to,dis};
    	head[from]=tot;
    }
    
    void dfs(int x,ll val)
    {
    	dis[x]=val; vis[x]=1; m++;
    	for (int i=0;i<=LG;i++)
    		if (val&(1LL<<i)) cnt[i]++;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (!vis[v]) dfs(v,val^e[i].dis);
    			else vs.ins(val^dis[v]^e[i].dis);
    	}
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=m;i++)
    	{
    		int x,y; ll z;
    		scanf("%d%d",&x,&y);
    		scanf("%lld",&z);
    		add(x,y,z); add(y,x,z);
    	}
    	for (int i=1;i<=n;i++)
    		if (!vis[i])
    		{
    			memset(cnt,0,sizeof(cnt));
    			vs.clr(); m=0;
    			dfs(i,0);
    			for (int i=0;i<=LG;i++)
    				if (vs.flag[i])
    					ans=(ans+(1LL*m*(m-1)/2%MOD)*((1LL<<vs.siz-1)%MOD)%MOD*((1LL<<i)%MOD))%MOD;
    				else
    					ans=(ans+(1LL*cnt[i]*(m-cnt[i])%MOD)*((1LL<<vs.siz)%MOD)%MOD*((1LL<<i)%MOD))%MOD;
    		}
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    一、linux 挂起进程 nohup
    1.C#窗体和控件
    C#笔记——5.迭代器
    C#笔记——4.集合
    设计模式——3.观察者模式
    设计模式——2.策略模式
    Code基础——1.数据结构
    设计模式——1.模板方法
    C#笔记——3.泛型
    C#笔记——2.委托
  • 原文地址:https://www.cnblogs.com/stoorz/p/14622955.html
Copyright © 2011-2022 走看看