Codeforces 724G Xor-matic Number of the Graph

Problem

Codeforces

Solution

这道题目思路会比较像wc2011的xor,可能会更难一点,是一道线性基的好题

你得知道维护对角矩阵的线性基保证对于所有线性基,二进制任意一位最多只有一个为1。但由于构造对角矩阵的线性基时间复杂度为 O ( n l o g 2 n ) ,所以我们一般(包括下文中的代码)写的是维护上三角矩阵的线性基。如果这个线性基第i位至少有一个1,那么显然前者也会有。

首先可以用一个O(n)的dfs处理出所有的环的异或和,我们只需记录一个异或和即可。更形象地说,dfs就相当于搜成一棵树,开始的节点就是根节点,一旦碰到已经在树上的点,由于异或的性质,dis[x] xor w xor dis[y]就相当于这个环的异或和,当然了可能没计入的大环是可以由小环组合而来的。由于图不保证联通,那么分联通块计算。

我们先枚举这两个点,容易知道dis[x] xor dis[y]就是两点路径的异或和
按位来考虑贡献,设当前处理的是第i位,要有贡献,我们需要分情况讨论。
不妨记线性基有tot个,dis[x]第i位为0的个数有cnt[0]个,对cnt[1]也是同样的定义

首先如果两点路径的异或和第i位为0的话,那么第i位做贡献,必须异或一些环使得最后第i位为1。选中这一位的线性基,其他的任意组合。那么贡献为
( C c n t [ 0 ] 2 + C c n t [ 1 ] 2 ) 2 i 2 t o t 1

另一种情况,我们在线性基中找有没有第i位为1的,如果没有,那么就可以tot个任意组合,如果有,那么去掉它再任意组合
c n t [ 0 ] c n t [ 1 ] 2 f ? t o t 1 : t o t

Code

关于调试的一个小trick,编译的时候加-ftrapv命令,如果re就说明爆int/long long了

#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=100010,mod=1000000007;
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
struct data{int v,nxt;ll w;}edge[maxn<<2];
int n,m,p,sz,fac[70],q[maxn],head[maxn];
ll ans,dis[maxn],l[70];
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline void insert(int u,int v,ll w)
{
    edge[++p]=(data){v,head[u],w};head[u]=p;
    edge[++p]=(data){u,head[v],w};head[v]=p;
}
void input()
{
    int u,v;ll w;
    read(n);read(m);
    for(int i=1;i<=m;i++)
    {
        read(u);read(v);read(w);
        insert(u,v,w);
    }
}
void add(ll x)
{
    for(int i=63;~i;i--)
      if(x&(1ll<<i))
      {
        if(!l[i]){l[i]=x;return ;}
        x^=l[i];
      }
}
void dfs(int x,int pre)
{
    q[++sz]=x;
    for(int i=head[x];i;i=edge[i].nxt)
      if(edge[i].v!=pre)
      {
        if(~dis[edge[i].v]) add(dis[x]^edge[i].w^dis[edge[i].v]);
        else dis[edge[i].v]=dis[x]^edge[i].w,dfs(edge[i].v,x);
      }
}
void calc()
{
    int f,tot=0,cnt[2];
    ll now;
    for(int i=0;i<=63;i++) if(l[i]) tot++;
    for(int i=0;i<=63;i++)
    {
        cnt[0]=cnt[1]=f=0;
        for(int j=1;j<=sz;j++) cnt[(dis[q[j]]>>i)&1]++;
        for(int j=0;j<=63&&!f;j++) if(l[j]&(1ll<<i)) f=1;
        now=((ll)cnt[0]*(cnt[0]-1)/2+(ll)cnt[1]*(cnt[1]-1)/2)%mod;
        if(f)
        {
            if(tot) now=now*fac[tot-1]%mod;
            now=now*fac[i]%mod;ans=pls(ans,now);
        }
        now=(ll)cnt[0]*cnt[1];
        if(f) {if(tot) now=now*fac[tot-1]%mod;}
        else now=now*fac[tot]%mod;
        now=now*fac[i]%mod;ans=pls(ans,now);
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    input();fac[0]=1;
    memset(dis,0xff,sizeof(dis));
    for(int i=1;i<70;i++) fac[i]=pls(fac[i-1],fac[i-1]);
    for(int i=1;i<=n;i++)
      if(dis[i]==-1)
      {
        memset(l,0,sizeof(l));
        sz=dis[i]=0;dfs(i,0);calc();
      }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/as_a_kid/article/details/80465499
今日推荐