魔法弹(线性基)

魔法弹(线性基)

给出n个m位数,问它们张成的异或集合的大小。

关于线性基,这篇博文讲的太好了:

所谓线性基,就是线性代数里面的概念。一组线性无关的向量便可以作为一组基底,张起一个线性的向量空间,这个基地又称之为线性基。这个线性基的基底进行线性运算,可以表示向量空间内的所有向量,也即所有向量可以拆成基底的线性组合。
在ACM领域,线性基主要用来处理有关异或和的极值问题。根据异或按照二进制数位进行的方式,我们可以把一个数字拆成它的二进制表示形式,而这个二进制形式拆成一位一位的,可以用向量来表示。显然,一组线性无关的向量可以张起一个向量空间,我们同样可以考虑构造这样一组数字的二进制形式组成的线性基。在这个线性基里面,我可以通过基底的线性组合、异或运算,表示所有的异或结果。
要构造这样的线性基,首先要满足一个性质:任取向量组中两个向量a、b,把其中一个替换成a xor b,这组向量线性组合得到的线性空间相同。这个性质对于xor来说是显然的,因为xor偶数具有抵消性。有了这个性质,就可以利用它对加入的向量进行修改。基本思路是:对于一个新加入的数字,从最高为考试往后扫,如果某一位该向量是1,但是之前的向量组中没有一个这一位是1,那么这一位就放如这个数字并且break;如果之前已经有该位为1的,那么该数字异或该位之前对应的数字。最后的结果是,新加入的数字x,要么被添加到某一个二进制位,要么变成0,说明这个数字对应的二进制向量不属于极大线性无关组。
线性基构造完毕,我们就要求其极值。很多时候我们需要求一堆数字最大的异或和,这个时候就要用到线性基。线性基构造完毕之后,根据它的定义,任何线性基所张的向量空间的所有向量都能够表示为其线性组合。所以我们只需要找,这个线性基中最大的向量即可。做法就是,从高位开始取线性基的每一个基底,如果异或能够使得结果变大,那么就异或。同时,这个还支持有初始值的最大值,因为线性基可以表示所有的向量,所以即使有一个初始值也是能够一起计算最大值的。

补充一下:n个的线性基能张成\(2^n\)个不同的元素,同时,有\(2^{n-1}\)个元素满足第i位为1(当然前提是线性基中有第i位为1的数)。可以这样考虑:找到一个第i位为1的线性基x,那么剩下n-1个元素随便异或,能张成\(2^{n-1}\)个数。为了让第i位为1,这些数中第i位为0的数需要异或x,否则就不用异或x。因此,这\(2^{n-1}\)个数都一一对应一个第i位为1的数。

对于这道题,尝试按位考虑。对于一个位,由于在线性基张成的空间中有\(2^{n-1}\)个数这个位为1,因此它对总和的贡献是\(x2^{n-1}\),x表示这个位的权值。

#include <cstdio>
#include <cstring>
using namespace std;

typedef long long LL;
const int maxn=405, mod=1e9+7;
int n, m, A[maxn];
struct Bigint{
    int a[maxn];  //从低到高
    bool empty(){
        int flag=1;
        for (int i=0; i<=m; ++i) if (a[i]) flag=0;
        return flag;
    }
    Bigint(){ memset(a, 0, sizeof(a)); }
    Bigint(char *s):Bigint(){
        for (int i=0; i<m; ++i) a[m-i-1]=s[i]-48;
        for (int i=0; i<m; ++i) A[i]|=a[i]; }
    int& operator[](int x){ return a[x]; }
    int value(){
        int re=0;
        for (int i=m; ~i; --i)
            re=(re*2+a[i])%mod;
        return re;
    }
}p[maxn];
Bigint operator ^(const Bigint &a, const Bigint &b){
    Bigint ans;
    for (int i=0; i<=m; ++i) ans[i]=a.a[i]^b.a[i];
    return ans; }

int fpow(int a, int x){
    LL base=a, ans=1;
    for (; x; x>>=1, (base*=base)%=mod)
        if (x&1) (ans*=base)%=mod;
    return ans; }

int main(){
    scanf("%d%d", &n, &m);
    char s[maxn]; Bigint t;
    for (int i=1; i<=n; ++i){
        scanf("%s", s);
        t=Bigint(s);
        for (int j=m; ~j; --j){
            if (!t[j]) continue;
            if (p[j].empty()) p[j]=t;
            t=t^p[j];
        }
    }
    LL ans=0, cnt=0, mi=1;
    for (int i=0; i<=m; ++i) if (!p[i].empty()) ++cnt;
    for (int i=0; i<=m; ++i){
        if (i) mi<<=1; mi%=mod;
        if (!A[i]) continue;
        ans+=fpow(2, cnt-1)*mi; ans%=mod;
    }
    printf("%lld\n", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/MyNameIsPc/p/9744980.html

时间: 2024-07-30 12:53:56

魔法弹(线性基)的相关文章

Xor HYSBZ - 2115 (线性基)

Xor HYSBZ - 2115 题意:给一个树,求1到n的最长路径.这里的路径定义为异或和. 线性基~~ 1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 struct LiBase{ 5 ll a[63]; 6 //初始化 7 void init(){ 8 memset(a,0,sizeof(a)); 9 } 10 //插入 11 bool insert_(ll x){ 12 for(int

线性基小节

1.线性基的异或集合中每个元素的异或方案唯一. 2.线性基二进制最高位互不相同. 3.线性基中元素互相异或,异或集合不变. 摘自百度文库 线性基能相互异或得到原集合的所有相互异或得到的值. 线性基是满足性质1的最小的集合 线性基没有异或和为0的子集. 证明: 反证法:设线性基S={a1,a2...,an}: 若有子集a1^a2^...^at=0,则a1=a2^a3^...^at,则舍弃a1后一定能通过剩余的元素异或出所有需要a1参与异或的值.设Y=a1^X,因为{a1,a2,...,an}是一组

[BeiJing2011]元素[贪心+线性基]

2460: [BeiJing2011]元素 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1245  Solved: 652[Submit][Status][Discuss] Description 相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔法矿石炼制法杖的技术.那时人们就认识到,一个法杖的法力取决于使用的矿石.一般地,矿石越多则法力越强,但物极必反:有时,人们为了获取更强的法力而使用了很多矿石,却在炼制过程中

BZOJ 3105: [cqoi2013]新Nim游戏 [高斯消元XOR 线性基]

以后我也要用传送门! 题意:一些数,选择一个权值最大的异或和不为0的集合 终于有点明白线性基是什么了...等会再整理 求一个权值最大的线性无关子集 线性无关子集满足拟阵的性质,贪心选择权值最大的,用高斯消元判断是否和已选择的线性相关 每一位记录pivot[i]为i用到的行 枚举要加入的数字的每一个二进制为1的位,如果有pivot[i]那么就异或一下(消元),否则pivot[i]=这个数并退出 如果最后异或成0了就说明线性相关... #include <iostream> #include &l

【BZOJ2844】albus就是要第一个出场 线性基 高斯消元

#include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/43456773"); } 题意:需要注意的是空集(0)是天生被包括的,我为了这个WA了好久~拍了好久,醉了好久~ 题解: 首先有一个我并不知道是为什么(甚至不知道它对不对)的性质: 每一种权值会出现2的自由元(n-线性基个数)次方 次. 感性

[bzoj 2460]线性基+贪心

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2460 网上很多题目都没说这个题目的证明,只说了贪心策略,我比较愚钝,在大神眼里的显然的策略还是想证明一下才安心--所以这里记录一下证明过程. 贪心策略:按魔力值从大到小排序,从大往小往线性基里插,如果成功插入新元素,就选这个,如果插不进去,就不选这个. 证明: 设有n个材料,每个材料的属性值是x[1],x[2],...,x[n],魔力值是v[1],v[2],...,v[n],这里假设v已

BZOJ 2460 元素(贪心+线性基)

显然线性基可以满足题目中给出的条件.关键是如何使得魔力最大. 贪心策略是按魔力排序,将编号依次加入线性基,一个数如果和之前的一些数异或和为0就跳过他. 因为如果要把这个数放进去,那就要把之前的某个数拿出来,而这样交换之后集合能异或出的数是不会变的,和却变小了. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector

线性基 学习笔记

ps:做CF的时候碰到了一个线性基的概念,然后在网上学习了一下,发现相关的资料很少,所以打算来写一个我个人的理解. 线性代数中 有极大线性无关组和空间的基的概念.  线性基的性质与此类似. 首先来看一个问题: 给出N个数,要从中选出一个最大的子集,使得子集中的任意个元素异或值不为0. 这个和极大线性无关组有些类似.异或可以看出是模2域下的加法运算,如果把一个数转化为二进制,对应成一个由01构成的向量, 所有这些向量就构成了一个线性空间. 原问题就转化为求这个向量组的极大线性无关组,把这样一个极大

【线性基】hdu3949 XOR

给你n个数,问你将它们取任意多个异或起来以后,所能得到的第K小值? 求出线性基来以后,化成简化线性基,然后把K二进制拆分,第i位是1就取上第i小的简化线性基即可.注意:倘若原本的n个数两两线性无关,也即线性基的大小恰好为n时,异或不出零,否则能异或出零,要让K减去1. 这也是线性基的模板. #include<cstdio> #include<cstring> using namespace std; typedef long long ll; ll d[64],p[64]; int