线性基
性质:
(1):原序列里面的任意一个数都可以由线性基里面的一些数异或得到。
(2):线性基里面的任意一些数异或起来都不能得到0
(3):线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的
先放模板:
插入函数
void insert_(ll x)
{
for(int i=60;i>=0;i--){
if((x>>i)&1)
{
if(d[i]) x^=d[i];
else{
d[i]=x;
break;
}
}
}
}
查询第k小的异或值
void work()//处理线性基
{
for(int i=1;i<=60;i++)
for(int j=1;j<=i;j++)
if(d[i]&(1<<(j-1)))d[i]^=d[j-1];
}
ll k_th(ll k)
{
if(k==1&&tot<n)return 0;//特判一下,假如k=1,并且原来的序列可以异或出0,就要返回0,tot表示线性基中的元素个数,n表示序列长度
if(tot<n)k--;//类似上面,去掉0的情况,因为线性基中只能异或出不为0的解
work();
ll ans=0;
for(int i=0;i<=60;i++)
if(d[i]!=0)
{
if(k%2==1)ans^=d[i];
k/=2;
}
}
Operation
Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 3129 Accepted Submission(s): 972
Problem Description
There is an integer sequence a of length n and there are two kinds of operations:
- 0 l r: select some numbers from al...ar so that their xor sum is maximum, and print the maximum value.
- 1 x: append x to the end of the sequence and let n=n+1.
Input
There are multiple test cases. The first line of input contains an integer T(T≤10), indicating the number of test cases.
For each test case:
The first line contains two integers n,m(1≤n≤5×105,1≤m≤5×105), the number of integers initially in the sequence and the number of operations.
The second line contains n integers a1,a2,...,an(0≤ai<230), denoting the initial sequence.
Each of the next m lines contains one of the operations given above.
It's guaranteed that ∑n≤106,∑m≤106,0≤x<230.
And operations will be encrypted. You need to decode the operations as follows, where lastans denotes the answer to the last type 0 operation and is initially zero:
For every type 0 operation, let l=(l xor lastans)mod n + 1, r=(r xor lastans)mod n + 1, and then swap(l, r) if l>r.
For every type 1 operation, let x=x xor lastans.
Output
For each type 0 operation, please output the maximum xor sum in a single line.
Sample Input
1 3 3 0 1 2 0 1 1 1 3 0 3 4
Sample Output
1 3
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<map>
#include<set>
#include<stack>
#define debug cout<<"*"<<endl;
#define input(x) scanf("%d",&x)
#define output(x) printf("%d\n",x);
#define llinput(x) scanf("%lld",&x)
#define lloutput(x) printf("%lld\n",x);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<string,int> psi;
typedef pair<char,char> pcc;
const int mod=998244353;
const int INF=INT_MAX;
const long long LLINF=LONG_MAX;
const int maxn=5e5+7;
const double PI=acos(-1);
const int N=31;
int a[maxn],pos[maxn][N+7],val[maxn][N+7];
void Init(int index)
{
for(int i=0;i<=N;i++)
{
pos[index][i]=pos[index-1][i];
val[index][i]=val[index-1][i];
}
}
void insert_(int x,int index)
{
int tmp=index;
Init(index);
for(int i=N;i>=0;--i)
{
if(x&(1<<i))
{
if(val[index][i]==0)
{
val[index][i]=x;
pos[index][i]=tmp;
break;
}
if(pos[index][i]<tmp)
{
swap(val[index][i],x);
swap(pos[index][i],tmp);
}
x^=val[index][i];
}
}
}
int query(int l,int r)
{
int maxx=0;
for(int i=N;i>=0;--i)
{
if(pos[r][i]>=l)
{
maxx=max(maxx,maxx^val[r][i]);
}
}
return maxx;
}
int main()
{
int t;
input(t);
while(t--){
int n,m;
input(n);
input(m);
for(int i=1;i<=n;i++)
{
input(a[i]);
insert_(a[i],i);
}
int ans=0;
while(m--)
{
int oper;
input(oper);
if(oper&1)
{
input(a[++n]);
a[n]^=ans;
insert_(a[n],n);
}
else{
int l,r;
input(l);
input(r);
l=(l^ans)%n+1;
r=(r^ans)%n+1;
if(l>r)
swap(l,r);
ans=query(l,r);
output(ans);
}
}
}
return 0;
}
链接:https://ac.nowcoder.com/acm/problem/19928
来源:牛客网
[CQOI2013]新NIM游戏
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。
本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。 如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。
链接:https://ac.nowcoder.com/acm/problem/19928
来源:牛客网
输入描述:
第一行为整数k。即火柴堆数。
第二行包含k个不超过109的正整数,即各堆的火柴个数。
输出描述:
输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1。
示例1
输入
复制
6
5 5 6 6 5 5
输出
复制
21
分析:
根据尼姆博弈,堆数的火柴数量异或值为0为必输态
第一轮拿完后,剩下的火柴不存在一个子集,使得子集异或值为0
从第三轮开始为正常的Nim博弈
这道题没有-1的情况是因为先手可以把n-1堆拿走,剩下一堆后手只能干瞪眼,所以先手必赢
题目要求拿的火柴数目尽可能少,线性基内放的是大的值,所以利用贪心思想,从大到小建线性基,若当前值是可以通过线性基中若干个元素异或得到,则可以把这一堆拿出,使最后剩下的是异或值不为0
动态维护线性基判断即可
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<map>
#include<set>
#include<stack>
#define debug cout<<"*"<<endl;
#define input(x) scanf("%d",&x)
#define output(x) printf("%d\n",x);
#define llinput(x) scanf("%lld",&x)
#define lloutput(x) printf("%lld\n",x);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<string,int> psi;
typedef pair<char,char> pcc;
const int mod=998244353;
const int INF=1e9+7;
const int maxn=1e5+7;
const double PI=acos(-1);
const int N=3007;
ll d[maxn];
ll ans=0;
int a[maxn];
bool cmp(ll a,ll b){return a>b;}
bool insert_(int x)
{
for(int i=60;i>=1;--i)
{
if((x>>i)&1)
{
if(d[i]) x^=d[i];
else{
d[i]=x;
break;
}
}
}
return x>0;
}
int main()
{
int k;
input(k);
ll sum=0;
for(int i=1;i<=k;++i)
{
cin>>a[i];
sum+=a[i];
}
sort(a+1,a+k+1,cmp);
ans=0;
for(int i=1;i<=k;++i)
{
if(!insert_(a[i]))
{
ans+=a[i];
}
}
if(ans==sum)
{
ans=-1;
}
lloutput(ans);
return 0;
}
xor序列
链接:https://ac.nowcoder.com/acm/problem/17968
来源:牛客网
小a有n个数,他提出了一个很有意思的问题:他想知道对于任意的x, y,能否将x与这n个数中的任意多个数异或任意多次后变为y
输入描述:
第一行为一个整数n,表示元素个数
第二行一行包含n个整数,分别代表序列中的元素
第三行为一个整数Q,表示询问次数
接下来Q行,每行两个数x,y,含义如题所示
输出描述:
输出Q行,若x可以变换为y,输出“YES”,否则输出“NO”
示例1
输入
复制
5
1 2 3 4 5
3
6 7
2 1
3 8
输出
复制
YES
YES
NO
说明
对于(6,7)来说,6可以先和3异或,再和2异或
对于(2,1)来说,2可以和3异或
对于(3,8)来说,3不论如何都不能变换为8
备注:
对于100%的数据,n,Q<=105
保证所有运算均在int范围内
分析:
求出已知序列线性基,对每一对x,y判断x^y是否可以加入线性基内即可
#include<bits/stdc++.h>
using namespace std;
const int INF=INT_MAX;
const long long LLINF=LONG_MAX;
const int mod=1e6+3;
const int xmax=1e5+7;
typedef long long ll;
ll a;
ll d[100007];
bool sign=false;
void insert_(ll x)
{
for(int i=60;i>=0;i--){
if((x>>i)&1)
{
if(d[i]) x^=d[i];
else{
d[i]=x;
break;
}
}
}
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a;
insert_(a);
}
ll q;
cin>>q;
while(q--)
{
ll x,y;
cin>>x>>y;
x^=y;
for(int i=60;i>=0;i--)
{
if(x&(1ll<<i)){
if(d[i]) x^=d[i];
else break;
}
}
if(x)
cout<<"NO"<<endl;
else
cout<<"YES"<<endl;
}
return 0;
}