0
点赞
收藏
分享

微信扫一扫

线性基及例题 c++

灵魂跑者 2022-01-08 阅读 99

*首先先介绍异或

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个数组成的一个可重集S,求一个集合T\subseteq S,使T_1 xor T_2 xor ...xor T_{T.size}最大

样例:

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)

题目描述

这是一道模板题。

给由 n 个数组成的一个可重集S ,每次给定一个数 k,求一个集合 T\subseteq S,使得集合 T  在 S  的所有非空子集的不同的异或和中,其异或和  T_1 xor T_2 xor ...xor T_{T.size}是第 k 小的。

 1\leqslant n,m\leqslant10^5,0\leqslant S_i\leqslant2^{50}

输入格式

第一行一个数 n
第二行  n 个数,表示集合 S
第三行一个数 m ,表示询问次数。
第四行  m 个数,表示每一次询问的 k

输出格式

输出  m 行,对应每一次询问的答案,第 k 小的异或和。如果集合 S  的所有非空子集中,不同的异或和数量不足 k ,输出 -1

样例

输入

3
1 2 3
5
1 2 3 4 5

输出

0
1
2
3
-1

思路,先将获得的线性基排序,再根据线性基的性质,将线性基从小到大看作是二进制从小到答案的每一位,设线性基内有m个元素,若m<n,则最多有2^m种情况(线性基内异或得到的数与线性基外的数异或可以为0),若m=n,则最多有2^{m}-1种情况(由性质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

对于全部的数据:1\leqslant N\leqslant1000,1\leqslant Number_i\leqslant10^{18},1\leqslant Magic_i\leqslant10^{4}

思路:(贪心+线性基)先将按能量从大到小排序,再构造线性基,若数字符合条件进入线性基,则ans+=对应能量

具体代码如下

#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;
}
举报

相关推荐

0 条评论