CodeForces 724G Xor-matic Number of the Graph(线性基+组合计数)

G. Xor-matic Number of the Graph
time limit per test:2 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output

You are given an undirected graph, constisting of n vertices and m edges. Each edge of the graph has some non-negative integer written on it.

Let's call a triple (u, v, s) interesting, if 1 ≤ u < v ≤ n and there is a path (possibly non-simple, i.e. it can visit the same vertices and edges multiple times) between vertices u and v such that xor of all numbers written on the edges of this path is equal to s. When we compute the value s for some path, each edge is counted in xor as many times, as it appear on this path. It's not hard to prove that there are finite number of such triples.

Calculate the sum over modulo 109 + 7 of the values of s over all interesting triples.

Input

The first line of the input contains two integers n and m (1 ≤ n ≤ 100 000, 0 ≤ m ≤ 200 000) — numbers of vertices and edges in the given graph.

The follow m lines contain three integers ui, vi and ti (1 ≤ ui, vi ≤ n, 0 ≤ ti ≤ 1018, ui ≠ vi) — vertices connected by the edge and integer written on it. It is guaranteed that graph doesn't contain self-loops and multiple edges.

Output

Print the single integer, equal to the described sum over modulo 109 + 7.

Examples
Input
Copy
4 4
1 2 1
1 3 2
2 3 3
3 4 1
Output
Copy
12
Input
Copy
4 4
1 2 1
2 3 2
3 4 4
4 1 8
Output
Copy
90
Input
Copy
8 6
1 2 2
2 3 1
2 4 4
4 5 5
4 6 3
7 8 5
Output
Copy
62
Note

In the first example the are 6 interesting triples:

  1. (1, 2, 1)
  2. (1, 3, 2)
  3. (1, 4, 3)
  4. (2, 3, 3)
  5. (2, 4, 2)
  6. (3, 4, 1)
The sum is equal to 1 + 2 + 3 + 3 + 2 + 1 = 12.

In the second example the are 12 interesting triples:

  1. (1, 2, 1)
  2. (2, 3, 2)
  3. (1, 3, 3)
  4. (3, 4, 4)
  5. (2, 4, 6)
  6. (1, 4, 7)
  7. (1, 4, 8)
  8. (2, 4, 9)
  9. (3, 4, 11)
  10. (1, 3, 12)
  11. (2, 3, 13)
  12. (1, 2, 14)
The sum is equal to 1 + 2 + 3 + 4 + 6 + 7 + 8 + 9 + 11 + 12 + 13 + 14 = 90.

      大致题意:一个图,求所有三元组(u,v,s),表示从u走到v的一条路径,异或和为s。让你求所有三元组的s的和。相当于求任意两点之间所有路径的异或和之和。
        之前我们做过BZOJ 2115,大致已经知道了在图上找异或和怎么处理,这里也是类似。首先求出任意路径,再求出所有的环的异或和。但是本题要求计算所有的三元组的s的和,意味着不是构造线性基找最大,而是统计所有的组合。对于这个s的和,我们还是考虑计算贡献,即s最多是一个63位的二进制,对于每一位计算为1时的s有多少个。
       按照之前的方法,首先是找一条初始路径,由于是任意两点路径,所以直接枚举起点和终点肯定会超时。因此还是按照贡献的方式去做,计算特定第i位二进制时,统计这一位为0和1的起点和终点个数cnt[0]和cnt[1]。对于枚举的第i位二进制,如果它对最后结果有贡献,那么这一位最后肯定是1。所以统计这一位为1有多少种情况。我们可以分两种情况讨论:
         1、起点和终点第i位相同,这意味着中间必须经过一个第i位是1的环。于是我们在线性基里面找,看是否有这样的环。如果有,那么一定只有一个。假设线性基总共有r个向量,那么另外r-1个向量可取可以不取,对应2^(r-1)种方案。如果没有那么这一位不能产生贡献。
         2、起点和终点第i位不同,这意味着中间不能经过第i位位1的环。还是一样,看是否有这样的环。如果有,则除了这个环不能取,其他任意,对应2^(r-1)种方案;如果没有那么所有r个向量都可以任意,对应2^r种方案。

      计算出两种的方案数之和,还要算上起点和终点取的方案数,再乘以这一位对应的数值2^i,就是第i位的贡献。统计所有的即可。具体见代码:

#include<bits/stdc++.h>
#define mod 1000000007
#define LL long long
#define N 500010
using namespace std;

struct Linear_Basis
{
    LL b[63],nb[63],tot;

    void init()
    {
        tot=0;
        memset(b,0,sizeof(b));
        memset(nb,0,sizeof(nb));
    }

    bool ins(LL x)
    {
        for(int i=62;i>=0;i--)
            if (x&(1LL<<i))
            {
                if (!b[i]) {b[i]=x;break;}
                x^=b[i];
            }
        return x>0;
    }

    LL Max(LL x)
    {
        LL res=x;
        for(int i=62;i>=0;i--)
            res=max(res,res^b[i]);
        return res;
    }

    LL Min(LL x)
    {
        LL res=x;
        for(int i=0;i<=62;i++)
            if (b[i]) res^=b[i];
        return res;
    }

    void rebuild()
    {
        for(int i=62;i>=0;i--)
            for(int j=i-1;j>=0;j--)
                if (b[i]&(1LL<<j)) b[i]^=b[j];
        for(int i=0;i<=62;i++)
            if (b[i]) nb[tot++]=b[i];
    }

    LL Kth_Max(LL k)
    {
        LL res=0;
        for(int i=62;i>=0;i--)
            if (k&(1LL<<i)) res^=nb[i];
        return res;
    }

} LB;

LL s[N],w[N],loop[N],cnt[2],ans;
struct Edge{int y;LL w;};
vector<Edge> g[N];
vector<int> p;
int n,m,r,tot;
bool v[N];

void dfs(int x)
{
    v[x]=1;
    for(int i=0;i<g[x].size();i++)
    {
        int y=g[x][i].y;
        LL w=g[x][i].w;
        if (!v[y])
        {
            s[y]=s[x]^w,dfs(y);
            p.push_back(y);
        } else loop[++tot]=s[y]^s[x]^w;
    }
}

LL qpow(LL a,LL b)
{
    LL ans=1;
    while(b)
    {
        if(b&1)ans=ans*a%mod;
        a=a*a%mod; b>>=1;
    }
    return ans;
}

void calc()
{
    for(int i=0;i<=62;i++)
    {
        bool flag=0;
        cnt[0]=cnt[1]=0;
        for(int j=0;j<p.size();j++)
            cnt[(s[p[j]]>>i)&1]++;
        LL tmp=cnt[0]*(cnt[0]-1)/2+cnt[1]*(cnt[1]-1)/2; tmp%=mod;                //起点终点第i位相同的方案数
        for(int j=0;j<=62;j++) if (LB.b[j]&(1LL<<i)) {flag=1;break;}            //判断是否有向量第i位为1
        if (flag)
        {
            if (r) tmp=tmp*qpow(2,r-1)%mod;                                        //如果有那么统计
            ans=(ans+tmp*qpow(2,i)%mod)%mod;
        }
        tmp=cnt[0]*cnt[1]%mod;                                            //起点和终点第i为不同的方案数
        if (flag)
        {
            if (r) tmp=tmp*qpow(2,r-1)%mod;
        } else tmp=tmp*qpow(2,r)%mod;
        ans=(ans+tmp*qpow(2,i)%mod)%mod;
    }
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        ans=0;
        for(int i=1;i<=m;i++)
        {
            int u,v; LL p;
            scanf("%d%d%lld",&u,&v,&p);
            g[u].push_back(Edge{v,p});
            g[v].push_back(Edge{u,p});
        }
        for(int i=1;i<=n;i++)
            if (!v[i])
            {
                LB.init(); p.clear();                                    //不同连通分量的东西不能够混用
                tot=r=0; p.push_back(i); dfs(i);
                for(int j=1;j<=tot;j++) LB.ins(loop[j]);
                for(int j=0;j<=62;j++) if (LB.b[j]) r++;
                calc();
            }
        printf("%lld\n",ans);
    }
}

CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值