线性基

线性基

Tags:数学

作业部落链接:https://www.zybuluo.com/xzyxzy/note/1076707


前言

网上优秀博客:https://blog.sengxian.com/algorithms/linear-basis
机房大佬们的总结 yyb%yl%zsy orz

一、理解

所谓基大概就是指
在一个集合内定义一种运算,用基中的元素可以运算出所有用集合中的元素运算的结果
可以理解成基是一个集合的。。浓缩?

然后线性基呢就是这个运算是异或运算

二、性质

线性基中以每一位为最高位的1的数至多只有一个
所以把一个元素加入线性基中,那么

    if(x&(1<<j))
    {
        if(!p[j]){p[j]=x;break;}//一定要break!!
        else x^=p[j];
    }

三、题目

四、应用

  • 求最大异或和
  • 求K小异或和

一个技巧

  • 求1-n路径最大异或值就是任意一条1到n的路径异或上图中的一些环得到的

代码

最大异或和(较简单【模板】线性基

#include<iostream>
#include<cstdio>
#include<cstdlib>
#define ll long long
using namespace std;
ll read()
{
    char ch=getchar();ll h=0,t=1;
    while(ch!='-'&&(ch>'9'||ch<'0'))ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch>='0'&&ch<='9'){h=h*10+ch-'0';ch=getchar();}
    return h*t;	
}
ll N,A[70],ans;
int main()
{
    N=read();
    for(int i=1;i<=N;i++)
    {
        ll x=read();
        for(int j=63;j>=0;j--)
            if(x&(1LL<<j))
            {
                if(!A[j]){A[j]=x;break;}
                else x^=A[j];
            }
    }
    for(int i=63;i>=0;i--)
        if((ans^A[i])>ans)ans^=A[i];
    printf("%lld
",ans);
    return 0;
}

K小异或和(比较难以理解,纠结了一个晚上,想通了[HDU2949] XOR

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define ll long long
using namespace std;
ll read()
{
	char ch=getchar();ll h=0,t=1;
	while(ch!='-'&&(ch>'9'||ch<'0'))ch=getchar();
	if(ch=='-')t=-1,ch=getchar();
	while(ch>='0'&&ch<='9'){h=h*10+ch-'0';ch=getchar();}
	return h*t;	
}
ll T,p[100],Q[100],cnt,N,M,CC=0;
void Insert(ll x)
{
	for(ll i=63;i>=0;i--)
	{
		if(!(x>>i))continue;
		if(!p[i]){p[i]=x;return;}
		else x^=p[i];
	}
	CC=1;//表示线性基可以异或出0
}
void Rebuild()
{
	for(ll i=63;i>=0;i--)
		for(ll j=i-1;j>=0;j--)
			if(p[i]&(1LL<<j))
				p[i]^=p[j];
  /*
    重建线性基,使得p[i]某非最高位上有1当且仅当p[该位]=0
	使得能够保证异或上p[i]一定会使答案增大
   */
	for(ll i=0;i<=63;i++)
		if(p[i])Q[cnt++]=p[i];
}
void Query(ll k)
{
	if(k>=(1LL<<cnt)){puts("-1");return;}//超过所有能够异或出来的结果个数 (线性基中cnt个元素选或者不选)
	ll res=0;
	for(ll i=cnt-1;i>=0;i--)
		if(k&(1LL<<i))res^=Q[i];
	/*
	  在线性基Q[0]~Q[cnt-1]中,从小到大是
	  Q[0],Q[1],Q[1]^Q[0],Q[2],Q[2]^Q[0],Q[2]^Q[1],Q[2]^Q[0]^Q[1].....
	  所以第k大的组成元素就是所有k的二进制位上的数的异或和
	 */
	printf("%lld
",res);
}
int main()
{
	T=read();
	for(ll w=1;w<=T;w++)
	{
		N=read();cnt=0;CC=0;
		memset(p,0,sizeof(p));
		memset(Q,0,sizeof(Q));
		for(ll i=1;i<=N;i++)Insert(read());
		Rebuild();
		M=read();
		printf("Case #%lld:
",w);
		for(ll i=1;i<=M;i++)
		{
			ll k=read()-CC;//查询第k小异或和
			if(!k)puts("0");
			else Query(k);
		}
	}
	return 0;
}

原文地址:https://www.cnblogs.com/xzyxzy/p/8587085.html