例题1
考察最大值查询
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int mo = 1e3;
const int N = 2e5 + 5;
ll d[70];
void add(ll x)
{
for (int i = 50; i >= 0; i--)
{
if (x & (1ll << i)) //注意,如果i大于31,前面的1的后面一定要加ll
{
if (d[i])x ^= d[i];
else
{
d[i] = x;
break;//插入成功就退出
}
}
}
}
ll ans()
{
ll anss = 0;
for (int i = 50; i >= 0; i--) //记得从线性基的最高位开始
if ((anss ^ d[i]) > anss)anss ^= d[i];
return anss;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n, x;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> x;
add(x);
}
cout << ans();
return 0;
}
例题2
考察存在情况的判断
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int mo = 1e3;
const int N = 2e5 + 5;
ll b[70], d[70];
void add(ll x)
{
for (int i = 60; i >= 0; i--)
{
if (x & (1ll << i)) //注意,如果i大于31,前面的1的后面一定要加ll
{
if (d[i])x ^= d[i];
else
{
d[i] = x;
break;//插入成功就退出
}
}
}
}
bool query(int y)
{
for (int i = 60; i >= 0; i--)
{
if (y & (1ll << i)) //注意,如果i大于31,前面的1的后面一定要加ll
{
if (b[i])y ^= b[i];
else
{
b[i] = y;
return 0;
}
}
}
return 1;
}
ll ans()
{
ll anss = 0;
for (int i = 50; i >= 0; i--) //记得从线性基的最高位开始
if ((anss ^ d[i]) > anss)anss ^= d[i];
return anss;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n, x, y;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> x;
add(x);
}
int q;
cin >> q;
while (q--)
{
memcpy(b,d,sizeof d);
cin >> x >> y;
y^=x;
if (query(y))
{
cout << "YES\n";
}
else
{
cout << "NO\n";
}
}
return 0;
}
例题三
[TJOI2008]彩灯 - 洛谷
我们把开关看成原数列,而灯的开闭就是其二进制分解的结果,两个或者若干个开关异或的结果就是最终灯的开闭情况,用线性基表示全部结果,
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int mo = 2008;
const int N = 2e5 + 5;
ll b[70], d[70];
void add(ll x)
{
for (int i = 60; i >= 0; i--)
{
if (x & (1ll << i)) //注意,如果i大于31,前面的1的后面一定要加ll
{
if (d[i])x ^= d[i];
else
{
d[i] = x;
break;//插入成功就退出
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n, m, x;
cin >> n >> m;
char c;
while (m--)
{
x=0;
for (int i = 0; i < n; i++)
{
cin >> c;
if (c == 'O')
{
x|=1ll<<i;
}
}
add(x);
}
ll ans=1;
for(int i=0;i<=60;i++)
{
if(d[i]!=0)
{
ans<<=1;
ans%=mo;
}
}
cout<<ans;
return 0;
}
例题4
考察性质2,3. 根据题意,我们发现一个异或子段可以是前缀异或数组相异或得到的,所以线性基可以记载全部n个异或前缀数组的异或情况,性质2保证了它们不可能存在异或结果是0的情况,性质3保证了这些子段的异或值都能够被表示。而值得注意的是,如果全部n个数异或之后不为0,那么我们线性基一定能够将这n个数都囊括进去,再利用这个大的前缀异或值去“分解”剩余小的。可如果n个数异或之后是0,那么至少这一结果不满足线性基表示非零的性质,从而无法被加入,无法参与切割,最右端点也不再是n
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int mo = 2008;
const int N = 2e5 + 5;
ll a[N], d[70];
void add(ll x)
{
for (int i = 60; i >= 0; i--)
{
if (x & (1ll << i)) //注意,如果i大于31,前面的1的后面一定要加ll
{
if (d[i])x ^= d[i];
else
{
d[i] = x;
break;//插入成功就退出
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n, now=0;
cin >> n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
now^=a[i];
add(now);
}
if(now==0)
{
cout<<-1;
return 0;
}
ll ans=0;
for(int i=0;i<=60;i++)
{
if(d[i]!=0)
{
ans++;
}
}
cout<<ans;
return 0;
}