*首先先介绍异或
1^1=0
1^0=1
0^1=1
0^0=0
对于数组a[1],a[2],a[3],.......a[n],异或和
xor_sum=a[1]^a[2]^a[3]^.....^a[n];
再介绍线性基的一些基本性质:
1.可以通过异或 线性基 中的一些数得到原序列里面的任意一个数的异或;
2.原序列的任意子集异或和都能通过线性基里面的一些数异或得到;
3.线性基中的任意几个数的异或和不为0;
4.线性基里面的所有数异或得到原序列的子序列的最大异或和;
5.线性基里面的数的个数唯一,并且在满足1的 情况下,线性基里数的个数是最少的。
线性基的构造:
vector<ull> B;
void insert(ull x)
{
for(auto b:B)
x=min(x,b^x);
for(auto &b:B)
b=min(b,b^x);
if(x)
B.push_back(x);
}
题型:
1.最大异或和:(LibreOJ #113)
给定由n个数组成的一个可重集,求一个集合,使最大
样例:
input:
3
5 2 8
output:
15
思路:参考性质4
ac代码:
#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;
typedef unsigned long long ull;
ll n,m,k;
vector<ull> B;
void insert(ull x)
{
for(auto b:B)
x=min(x,b^x);
for(auto &b:B)
b=min(b,b^x);
if(x)
B.push_back(x);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
vector<ull> a(n);
for(auto &x:a)
cin>>x;
for(auto x:a)
insert(x);
ull ans=0;
for(auto x:B)
ans^=x;
cout<<ans<<endl;
return 0;
}
2.k大异或和(LibreOJ #114)
题目描述
这是一道模板题。
给由 个数组成的一个可重集 ,每次给定一个数 ,求一个集合 ,使得集合 在 的所有非空子集的不同的异或和中,其异或和 是第 小的。
输入格式
第一行一个数 。
第二行 个数,表示集合 。
第三行一个数 ,表示询问次数。
第四行 个数,表示每一次询问的 。
输出格式
输出 行,对应每一次询问的答案,第 小的异或和。如果集合 的所有非空子集中,不同的异或和数量不足 ,输出 。
样例
输入
3
1 2 3
5
1 2 3 4 5
输出
0
1
2
3
-1
思路,先将获得的线性基排序,再根据线性基的性质,将线性基从小到大看作是二进制从小到答案的每一位,设线性基内有个元素,若,则最多有种情况(线性基内异或得到的数与线性基外的数异或可以为0),若,则最多有种情况(由性质3可知不可能通过异或得到0)
代码实现:
#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
using namespace std;
typedef unsigned long long ull;
ll n,m,k;
vector<ull> B;
void insert(ull x)
{
for(auto b:B)
x=min(x,b^x);
for(auto &b:B)
b=min(b,b^x);
if(x)
B.push_back(x);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
vector<ull> a(n);
for(auto &x:a)
cin>>x;
sort(a.begin(),a.end());
for(auto x:a)
insert(x);
int bs=B.size();
ll sum=pow(2,bs);
sort(B.begin(),B.end());
cin>>m;
for(int v=0;v<m;v++)
{
ll x;
cin>>x;
if(x>sum||x<=0)
cout<<-1<<endl;
else
{
ll ans=0;
int i=0;
if(bs!=n)
x--;
while(x)
{
if(x%2==1)
ans^=B[i];
x/=2;
i++;
}
cout<<ans<<endl;
}
}
return 0;
}
洛谷 P4570 [BJWC2011]元素
题意:有序列a[1],a[2],.....a[n],每个数对应相应能量,求在子序列异或和最大的同时子序列中对应的能量和也最大
输入输出样例
输入 #1
3 1 10 2 20 3 30
输出 #1
50
对于全部的数据:
思路:(贪心+线性基)先将按能量从大到小排序,再构造线性基,若数字符合条件进入线性基,则+=对应能量
具体代码如下
#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
ll n,m,k;
vector<ull> B;
int insert(ull x)
{
for(auto b:B)
x=min(x,b^x);
for(auto &b:B)
b=min(b,b^x);
if(x)
{
B.push_back(x);
return 1;
}
else
return 0;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
vector<pair<int,ull> >a(n);
for(auto &x:a)
{
cin>>x.s>>x.f;
x.f=-x.f;
}
sort(a.begin(),a.end());
int ans=0;
for(auto x:a)
{
if(insert(x.s)==1)
ans-=x.f;
}
cout<<ans<<endl;
return 0;
}