【bzoj2115】 Xor

www.lydsy.com/JudgeOnline/problem.php?id=2115 (题目链接)

题意:给出一张图,可能有重边和自环,在图中找出一条从1~n的路径,使得经过的路径的权值的异或和最大,每条边可以重复经过并且重复计算异或和。

Solution 
  刚看到这道题,想了10分钟完全没有思路,于是就膜了题解。 
  我们先把图看成一棵树,那么我们所需要找出的路径可以分成一些环和一条从1~n的树上路径,至于树是怎么构造的以及那条从1~n的路径是怎么走的并不重要,因为我们可以通过在总路径上补环来增大异或和。于是这个问题就转化为了如何在环的异或和中选出一些使得这些值异或上从1~n的路径的异或和最大。 
  于是我们先dfs一遍把环以及它的异或和抠出来,当然这些环可能并不完全,但它们可以通过互相异或来异或出没有得到的环。如果发现当前节点x的目标节点e[i].to已经被走过了,那么这个环的异或和就是dis[x]^dis[e[i].to]^e[i].w。 
  怎么求最大异或和呢?这需要用到一个很神的东西:线性基 
   
什么是线性基:

  若干数的线性基是一组数a1,a2,…an,其中ax的最高位的1在第x位。通过线性基中元素xor出的数的值域与原来的数xor出数的值域相同。 
  线性基主要有2个优秀的性质: 
  可以O(k)(二进制的位数)的求出能异或出的最大值,或者是判断一个数能否被异或出来。

线性基的构造:

  线性基的构造有两种: 
  第一是高斯消元。通过高斯消元将二进制矩阵消成上三角矩阵或者是对角矩阵,当然消成后者时间复杂度略高,但也更为方便,求解异或和最大时一路for下去就可以了。而上三角矩阵需要判断如果答案异或上这个值能否使答案增大。 
  第二种就是一种拟阵贪心,详情参见jump大爷的博客——www.cnblogs.com/ljh2000-jump/p/5869991.html,因为证明太高深,这里也就不再赘述。

  所以这道题就很好做了对吧。

代码:

// bzoj2115
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define Pi 3.1415926535898
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=50010,maxm=100010;
struct edge {int to,next;LL w;}e[maxm<<1];
LL dis[maxn],c[maxm<<2],bin[65];
int vis[maxn],n,m,tot,sum,cnt,head[maxn];

void link(int u,int v,LL w) {
    e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=w;
    e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;e[cnt].w=w;
}
void dfs(int x) {
    vis[x]=1;
    for (int i=head[x];i;i=e[i].next) {
        if (!vis[e[i].to]) {
            dis[e[i].to]=dis[x]^e[i].w;
            dfs(e[i].to);
        }
        else c[++sum]=dis[x]^dis[e[i].to]^e[i].w;
    }
}
void Gauss() {
    for (LL i=bin[60];i;i>>=1) {
        int j=tot+1;
        while (j<=sum && !(c[j]&i)) j++;
        if (j==sum+1) continue;
        swap(c[++tot],c[j]);
        for (j=1;j<=sum;j++) if (j!=tot && c[j]&i) c[j]^=c[tot];
        //for (int k=j+1;k<=sum;k++) if (c[k]&i) c[k]^=c[tot];
    }
}
int main() {
    bin[0]=1;for (int i=1;i<=60;i++) bin[i]=bin[i-1]<<1;
    scanf("%d%d",&n,&m);
    for (int u,v,i=1;i<=m;i++) {
        LL w;
        scanf("%d%d%lld",&u,&v,&w);
        link(u,v,w);
    }
    dfs(1);
    Gauss();
    LL ans=dis[n];
    for (int i=1;i<=tot;i++) ans=max(ans,ans^c[i]);
    printf("%lld\n",ans);
    return 0;
}

  

时间: 2024-10-11 12:48:31

【bzoj2115】 Xor的相关文章

【BZOJ-2115】Xor 线性基 + DFS

2115: [Wc2011] Xor Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 2142  Solved: 893[Submit][Status][Discuss] Description Input 第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目. 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边. 图中可能有重边或自环. Output 仅包含一个整数,表示最大

【BZOJ2337】Xor和路径(高斯消元)

[BZOJ2337]Xor和路径(高斯消元) 题面 BZOJ 题解 我应该多学点套路: 对于xor之类的位运算,要想到每一位拆开算贡献 所以,对于每一位拆开来看 好了,既然是按位来算 我们就只需要计算从\(1\)号点开始 到\(n\)的路径中,路径的异或和为\(1\)的概率 显然没法算 还是一样的 考虑高斯消元 对于每一个节点\(i\) \[f[i]=\sum_{w(u,i)=1}\frac{1-f[u]}{op[u]}+\sum_{w(u,i)=1}\frac{f[u]}{op[u]}\] 其

【HDU3949】XOR

[题目大意] 给定一个数组,求这些数组通过异或能得到的数中的第k小是多少. 传送门:http://vjudge.net/problem/HDU-3949 [题解] 首先高斯消元求出线性基,然后将k按照二进制拆分即可. 注意当高斯消元结束后若末尾有0则第1小是0 特判一下然后k--. 然后HDU输出long long是用%I64d 无论C++还是G++都是.(虽然我用了lld也AC了) 1 #include<iostream> 2 #include<cstdio> 3 #includ

bzoj2115【WC2001】Xor

2115: [Wc2011] Xor Time Limit: 10 Sec  Memory Limit: 259 MB Submit: 2059  Solved: 856 [Submit][Status][Discuss] Description Input 第一行包括两个整数N和 M, 表示该无向图中点的数目与边的数目. 接下来M 行描写叙述 M 条边,每行三个整数Si.Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边. 图中可能有重边或自环. Output 仅包括一个整数,

【bzoj2115】[Wc2011] Xor【高斯消元】

题目大意:给出一个无向有权图,找出一条从1到n的路径,使得路径上权值的异或和最大,路径可以重复走 Input 第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目. 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边. 图中可能有重边或自环. Output 仅包含一个整数,表示最大的XOR和(十进制结果) . Sample Input 5 71 2 21 3 22 4 12 5 14 5 35 3 44 3 2 Sample

【BZOJ2115】【Wc2011】 Xor 线性基 异或最长路

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43410545"); } 题意:找一条异或最长路. 题解:先随便来一条路径,然后我们发现这条路径上可以随便加简单环(不管有没有共点共边). 就是因为可以先从某点走到环上来一圈再走回来,这样来去的路径被搞没了,简直污得不行. 然后我们可以用线性基来决定去

【HNOI2011】XOR和路径

pre.cjk { font-family: "Droid Sans Fallback", monospace } p { margin-bottom: 0.25cm; line-height: 120% } a:link { } 和期望有关的题一脸懵逼QAQ 题目要求的是异或和的期望,所以就把二进制的每一位都搞一遍,最后答案就是每一位的期望和. 然后对于每一位,推出一个很鬼畜的DP方程:f[i]表示i这个点到n的概率. f[n][n]=1;然后枚举i,枚举i连的每个点,如果这条路的边

【HDU3949】XOR 线性基

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43448493"); } 题意:给若干个数让你异或,然后询问第k大的异或和. 题解: 先搞出来线性基,然后第k大的异或和就是: 把k二进制拆分,第i位上有1,就把第i个线性基异或进来. 原因: 因为线性基是一堆高位上的1(或许有一些位动不了),然后把这

【Hdu4825】Xor Sum(01字典树)

Description Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeus 发起M次询问,每次询问中包含一个正整数 S ,之后 Zeus 需要在集合当中找出一个正整数 K ,使得 K 与 S 的异或结果最大.Prometheus 为了让 Zeus 看到人类的伟大,随即同意 Zeus 可以向人类求助.你能证明人类的智慧么? Solution 01字典树入门题 Code #include