[原创]HDU 3949 XOR [线性基|高斯消元]【数学】

2017-01-23 21:38:29 Tabris_ 阅读数:662


博客爬取于2020-06-14 22:41:59
以下为正文

版权声明:本文为Tabris原创文章,未经博主允许不得私自转载。
https://blog.csdn.net/qq_33184171/article/details/54695958


题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3949
--------------------------------------------------------------------------------.
碑商家客流量预测大赛》
XOR

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2533 Accepted Submission(s): 858

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.

Author
elfness

--------------------------------------------------------------------------------.
题目大意:
就是给你长度为N的学列,有Q次查询,每次查询这写序列中能异或出来的第k小的值

解题思路
本题是一个线性基的入门题。
线性基的介绍或者参考2014国家集训队论文《浅谈线性相关》

其实线性基的求解过程就是一个高斯消元,它构建了一个二维的空间,N*bits这么大,
通过列与列相消,求解出基向量也就是空间的极大无关组,通过这几个元素能得到含盖空间中所有元素的无关组.

本题求的就是这个无关组能构建出来的第K小的值.

求这个值我们可以类比最理想情况下的无关组,

1 0 0 0 0 0 … 0p1p_1
0 1 0 0 0 0 … 0p2p_2
0 0 1 0 0 0 … 0p3p_3
0 0 0 1 0 0 … 0p4p_4
0 0 0 0 1 0 … 0p5p_5
0 0 0 0 0 1 … 0p6p_6

0 0 0 0 0 0 … 0pnp_n

那么对于第K小的值就是将K二进制展开,第ii位上为1,就在结果上异或pnip_{n-i}就好了,

至于为什么对呢?

因为是将K二进制展开,而pip_i这种特殊数据下,得出的刚好是K,这显然是正确的。

但是无关组不可能都是这么理想的

但这个规律对于普通的情况也是使用的,不太好表述,就是如果其他位上如果有1 的话,对于异或出来的结果的集合中的顺序是不影响的。 可以写两个数据验证一下,,,

附本题代码
--------------------------------------------------------------------------------.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# include <bits/stdc++.h>
using namespace std;
typedef long long int LL ;
const int N = 10000+7;
/***********************************************************************/

int cnt;
LL a[N],p[65];

void Guass(int n){
memset(p,0,sizeof(p));
for(int i=1;i<=n;i++)
for(int j=63;j>=0;j--)
if ((a[i]>>j)&1){
if (p[j]) a[i]^=p[j];
else {p[j]=a[i]; break;}
}

for(int i=63;i>=0;i--){
if(!p[i]) continue;
for(int j=i+1;j<=62;j++)
if((p[j]>>i)&1) p[j]^=p[i];
}

cnt=0;
for(int j=0;j<=63;j++) if (p[j]) p[cnt++]=p[j];
}

int main(){
int _ = 1,kcase;
while(~scanf("%d",&_)){
kcase = 0;
while(_--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%I64d",&a[i]);
Guass(n);
printf("Case #%d:\n",++kcase);
int q;LL x;
scanf("%d",&q);
while(q--){
scanf("%I64d",&x);

if(n!=cnt) x--;
if(x>=(1ll<<cnt)) puts("-1");
else {
LL ans = 0;
for(int i=0;i<=63;i++){
if((x>>i)&1) ans^=p[i];
}
printf("%I64d\n",ans);
}
}
}
}
return 0;
}