在这里学习了线性基:https://www.cnblogs.com/wfj2048/p/6547789.html
主要还是应用在异或运算中比较多,比如一个数的集合S,取出它的一个子集,将这个子集中的数字异或起来得到一个数字,我们就可以得到这样一个异或值的集合,然后我们就可以用一组线性基X来代替这个集合S,他们所得到的异或集合一样,且集合X的大小最小,如果|X|==|S|则S为线性不相关,否则S线性相关。
然后就可以用线性基来快速地求,异或集合的最小值,最大值,第k小值等。
例题:
hdu3949
XOR
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4887 Accepted Submission(s): 1718
Problem Description
XOR is a kind of bit operator, we define that as follow: for two binary base number A and B, let C=A XOR B, then for each bit of C, we can get its value by check the digit of corresponding position in A and B. And for each digit, 1 XOR 1 = 0, 1 XOR 0 = 1, 0 XOR 1 = 1, 0 XOR 0 = 0. And we simply write this operator as ^, like 3 ^ 1 = 2,4 ^ 3 = 7. XOR is an amazing operator and this is a question about XOR. We can choose several numbers and do XOR operatorion to them one by one, then we get another number. For example, if we choose 2,3 and 4, we can get 2^3^4=5. Now, you are given N numbers, and you can choose some of them(even a single number) to do XOR on them, and you can get many different numbers. Now I want you tell me which number is the K-th smallest number among them.
Input
First line of the input is a single integer T(T<=30), indicates there are T test cases.
For each test case, the first line is an integer N(1<=N<=10000), the number of numbers below. The second line contains N integers (each number is between 1 and 10^18). The third line is a number Q(1<=Q<=10000), the number of queries. The fourth line contains Q numbers(each number is between 1 and 10^18) K1,K2,......KQ.
Output
For each test case,first output Case #C: in a single line,C means the number of the test case which is from 1 to T. Then for each query, you should output a single line contains the Ki-th smallest number in them, if there are less than Ki different numbers, output -1.
Sample Input
2
2
1 2
4
1 2 3 4
3
1 2 3
5
1 2 3 4 5
Sample Output
Case #1:
1
2
3
-1
Case #2:
0
1
2
3
-1
Hint
If you choose a single number, the result you get is the number you choose. Using long long instead of int because of the result may exceed 2^31-1.
题意:求一个数组的异或集合中第k小。
思路:模板即可,注意判断集合是否线性相关,如果线性相关则0也可以达到。
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
typedef long long ll;
ll p[62];
ll d[62];
int cnt;
void add(ll x){
for (ll i=62;i>=0;--i)
if (x>>i&1){
if (!p[i]){ p[i]=x; break; }
x^=p[i];
}
return;
}
void rebuild(){
for (ll i=62;i>=0;--i)
for (ll j=i-1;j>=0;--j)
if (p[i]>>j&1) p[i]^=p[j];
for (ll i=0;i<=62;++i) if (p[i]) d[cnt++]=p[i];
return;
}
ll query_kth(ll k){
if (k>=(1LL<<cnt)) return -1; ll res=0;
for (ll i=62;i>=0;--i)
if (k>>i&1) res^=d[i];
return res;
}
int main()
{
int t;
scanf("%d",&t);
int cas = 0;
while(t--)
{
printf("Case #%d:\n",++cas);
memset(p,0,sizeof(p));
cnt = 0;
int n;
scanf("%d",&n);
ll x;
for(int i = 0;i<n;i++)
{
scanf("%lld",&x);
add(x);
}
rebuild();
// for(int i = 0;i<cnt;i++)cout<<"i = "<<i<<" d = "<<d[i]<<endl;
int Q;
ll k;
scanf("%d",&Q);
while(Q--)
{
scanf("%lld",&k);
if(n>cnt)k--;
if(k==0)printf("0\n");
else printf("%lld\n",query_kth(k));
}
}
}
牛客网
Wannafly挑战赛14
链接:https://www.nowcoder.com/acm/contest/81/E
来源:牛客网
无效位置
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给一个1-base数组{a},有N次操作,每次操作会使一个位置无效。一个区间的权值定义为这个区间里选出一些数的异或和的最大值。求在每次操作前,所有不包含无效位置的区间的权值的最大值。
输入描述:
第一行读入一个正整数(1 <= n <= 105)
第二行读入n个正整数,第i个表示a[i](0<= a[i] <= 109)
第三行读入n个正整数,第i个表示x[i]即第i次操作的位置,保证x[i]互不相同。
输出描述:
输出n行答案
示例1
输入
复制
10 169 816 709 896 58 490 97 254 99 796 4 2 3 10 5 6 1 8 9 7
输出
复制
1023 994 994 994 490 490 254 254 99 97
题意:给出一个数组,n次每次从数组中使一个位置上的数无效,并且求使它无效前,数组中不包含无效数字的区间的异或集合中最大值。
思路:离线处理,并查集,线性基。反过来做,并查集合并的同时线性基也合并。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
ll a[maxn];
int step[maxn];
bool used[maxn];
int mp[maxn];
ll p[maxn*3][63];
ll ans = 0;
ll res[maxn];
int cnt;
void add(ll x,ll * p){
for (ll i=62;i>=0;--i)
if (x>>i&1){
if (!p[i]){ p[i]=x; break; }
x^=p[i];
}
return;
}
ll getmax(ll * p){
ll res=0;
for (ll i=62;i>=0;--i)
if (res<(res^p[i])) res^=p[i];
return res;
}
int Rank[maxn],par[maxn];
void Init(int n){ //初始哈并查集
for(int i = 1; i <= n; i++){
Rank[i] = 0;
par[i] = i;
}
}
int find(int x){ //查找根节点
int root = x;
while(root != par[root]) root = par[root];
while(x != root){
int t = par[x];
par[x] = root;
x = t;
}
return root;
}
void unite(int x, int y){ //合并集合
x = find(x);
y = find(y);
int x1 = mp[x],y1 = mp[y];
++cnt;
for(int i = 0;i<63;i++)
{
add(p[x1][i],p[cnt]);
add(p[y1][i],p[cnt]);
}
ans = max(ans,getmax(p[cnt]));
if(Rank[x] < Rank[y]){
par[x] = y;
mp[y] = cnt;
}
else{
par[y] = x;
mp[x] = cnt;
if(Rank[x] == Rank[y]) Rank[x]++;
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i = 1;i<=n;i++)
scanf("%d",&step[i]);
Init(n);
ans = 0;
for(int i = n;i>=1;i--)
{
int ps = step[i];
used[ps] = 1;
mp[ps] = ++cnt;
ans = max(ans,a[ps]);
for(ll j = 62;j>=0;j--)
{
if(a[ps]>>j&1)
{
p[cnt][j] = a[ps];
break;
}
}
if(used[ps-1])
{
unite(ps,ps-1);
}
if(used[ps+1])
{
unite(ps,ps+1);
}
res[i] = ans;
}
for(int i = 1;i<=n;i++)printf("%d\n",res[i]);
}