CF724G 【Xor-matic Number of the Graph】

  • 题目就不翻译了吧,应该写的很清楚了。。。

  • 首先 \(,\) 不懂线性基的可以戳这里。知道了线性基\(,\) 但是从来没有写过线性基和图论相结合的\(,\) 可以戳这里
  • \(,\) 点完了这些前置技能之后,我们就可以来愉快的切题啦!

正片\(:\)

  • 类比\([WC\) \(2011]\) 最大\(xor\)和路径\(,\) 我们肯定要找环\(,\) 找完环后再用环去构造线性基\(,\) 因为还是那句话嘛\(:\) 任何一条复杂路径\(,\) 都能有起始两点的一条简单路径再加上若干个环组成

  • 那么接下来的问题就是\(:\)如何求出亦或值的和?

  • \(en\)~~~,直接依据线性基,求出可以被这个线性基构造出的所有亦或值的和,然后相加,呃,是个好方法。

  • 可惜\(,\) 很不幸\(,\) \(xor\)不满足分配律。然后我又冥思苦想了2天,终于找到了\(n\)个数中,任取几个数的亦或和的和”这个问题的\(O(n*63*63)\)的做法,高兴了好一阵子,然后发现。。。哎\(,\) 现在想想还是难受啊。

  • 然后痛定思痛\(,\) 改变视角——\(woc\),原来这么简单\(!\)

  • 不再考虑每一对数的\(xor\)和,我们改变目标 \(,\)去求:对于每一对点的每一位,有多少种方案能使该位的\(xor\)和位1

  • 对于我们原先就求出来的\(d[1]\)\(d[n]\) \((\)就是各个点到1号点的一条简单路径的\(xor\)\()\) \(,\) 我们要想求出\(u\)\(v\)的一条简单路径\(,\) 只需要用\(d[u]\) \(xor\) \(d[v]\)即可\(,\) 因为重复走到路径会因为\(xor\)操作而抵消掉。

  • 若线性基中有\(cnt\)个非零位,则一共会产生\(2^{cnt}\)个不同的\(xor\)值。

  • 知道了这些以后\(,\) 我们就可以开始讨论了\(:\)

  • \(1.\) \(d[u]\) \(xor\) \(d[v]\)\(k\)位为1:这样的话,我们就需要不取第\(k\)个线性基\((\)因为只有第\(k\)位为1了才会对答案产生贡献嘛\(!\) \()\) \(,\) 这样的话,该项对于答案的贡献即为\(:\) \(2^{k}*2^{cnt-1}\)

\(2.\) \(d[u]\) \(xor\) \(d[v]\)\(k\)位为0\(:\) 显然,一定要取\(k\)个线性基\(,\) 对答案的贡献即为\(:\) \(2^k*2^{cnt-1}\)

  • 尽管这样\(,\) 时间复杂度依然是\(O(n^2*63)\)\(,\) 吃不消\(,\) 那么哪里还有优化的空间呢\(?\)

  • 注意到\(,\) \(d[i]\)\(k\)位的值是固定的\(,\) 那么我们就可以不单独讨论\("d[u]\) xor \(d[v]"\)对于答案的贡献\(,\) 直接讨论\(d[u]\)对于答案的贡献。

  • 先统计出第\(k\)位为\(0\)的数的个数\(,\) 我们将其记为\(x\) \(,\) 再统计出第\(k\)位为\(1\)的数的个数\(,\) 记为\(y\) \(,\) 总共有\(point\)个点。

  • 分为\(4\)种情况讨论\(:\)

  • \(1.\) \(d[i]\)的第\(k\)位为\(1\) \(,\) 线性基中有第\(k\)位为\(1\)的数\(:\) 此时我们有两种选择:\(a.\) 选线性基中的\(,\) 另一个点选择第\(k\)位为\(0\)的。 \(b.\) 不选线性基中的\(,\) 另一个点选择第\(k\)位为\(1\)的。总体对于答案的贡献为:\(2^k*2^{cnt-1}*(point-1)\) \(,\)\(1\)就是为了把自己给去掉。

\(2.\) \(d[i]\)的第\(k\)位为\(1\) \(,\) 线性基中没有第\(k\)位为\(1\)的数\(:\) 这个时候另一个点只能取第\(k\)位为\(0\)\(,\) 所以总贡献为\(:\) \(2^k*2^{cnt}*x\)

\(3.\) \(d[i]\)的第\(k\)位为\(0\) \(,\) 线性基中有第\(k\)位为\(1\)的数\(:\) 一样的\(,\) 两种选择\(:\) \(a.\) 选线性基中的\(,\) 另一个点选择第\(k\)位为\(0\)的。 \(b.\) 不选线性基中的\(,\) 另一个点选择第\(k\)位为\(1\)的。总贡献为\(:\) \(2^k*2^{cnt-1}*(point-1)\)

\(3.\) \(d[i]\)的第\(k\)位为\(0\) \(,\) 线性基中没有第\(k\)位为\(1\)的数\(:\) 另一个点只能取第\(k\)位为\(1\)\(,\) 总贡献\(:\) \(2^k*2^{cnt}*y\)

  • 最后别忘记给答案除个\(2\)\(OK\)\(!(\)要用逆元哦\()\)

代码\(:\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=100005,M=400005,p=1e9+7,inv=500000004;
ll b[65],dist[M],d[N],a[N],two[65],s[N],z,ans,cnt,point;
int head[N],vet[M],nxt[M],c[65],n,m,x,y,tot;
bool vis[N],used[M];
void add(int x,int y,ll z){
    nxt[++tot]=head[x];
    vet[tot]=y;
    head[x]=tot;
    dist[tot]=z;
}
void insert(ll x){
    for (int i=62;i>=0;i--)
        if (x>>i)
            if (b[i]) x^=b[i];
            else {b[i]=x; break;}
}
void dfs(int u){
    a[++point]=d[u]; vis[u]=true;
    for (int i=head[u];i;i=nxt[i]){
        int v=vet[i];
        if (!vis[v]){
            d[v]=d[u]^dist[i];
            dfs(v);
        } else
            if (!used[i^1]){
                used[i^1]=true;
                insert(d[u]^d[v]^dist[i]);
            }
    }
}
ll calc(){
    ll ans=0;
    for (int j=0;j<=62;j++){
        ll x=0,y=0,flag=0;
        for (int i=1;i<=point;i++)
            if (a[i]>>j&1) x++; else y++;
        for (int i=0;i<=62;i++)
            if (b[i]>>j&1) flag=1;
        for (int i=1;i<=point;i++)
            if (a[i]>>j&1)
                if (flag)
                    (ans+=two[cnt-1]*(ll)(point-1)%p*two[j]%p)%=p;
                else
                    (ans+=two[cnt]*y%p*two[j]%p)%=p;
            else
                if (flag)
                    (ans+=two[cnt-1]*(ll)(point-1)%p*two[j]%p)%=p;
                else
                    (ans+=two[cnt]*x%p*two[j]%p)%=p;
    }
    return ans;
}
int main(){
    scanf("%d %d",&n,&m); tot=1;
    for (int i=1;i<=m;i++){
        scanf("%d %d %lld",&x,&y,&z);
        add(x,y,z); add(y,x,z);
    }
    two[0]=1;
    for (int i=1;i<=64;i++)
        two[i]=two[i-1]*2%p;
    for (int i=1;i<=n;i++)
        if (!vis[i]){
            memset(b,0,sizeof(b));
            memset(c,0,sizeof(c));
            point=0,cnt=0;
            dfs(i);
            for (int j=0;j<=62;j++) cnt+=(b[j]>0);
            (ans+=calc())%=p;
        }
        ans=ans*inv%p;
        printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/WR-Eternity/p/10291631.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值