您的位置:首页 > 其它

HDOJ 3949 XOR (高斯消元 + XOR线性基)

2016-02-17 20:21 281 查看
点击打开链接

题意:给出n个数,任选大于等于1个数进行异或,得到的结果加入一个集合,问这个集合中第k大的数是多少?

异或类比加减,把n个数的二进制从上到下排成一个矩阵,化成行最简式(每一行的第一个1所在的列上只有这一个1)。那么非零的行所对应的数,是原来那n个数的极大无关组。极大无关组的定义是:线性无关和原来的数都可以由极大无关组中的数来表示。行最简式显然线性无关,并且原来这么得到行最简式的现在就可以怎么回去,一定是可以表示出原来那些数的。化成行最简式的伪代码如下:

for i = 所有数

{

p = i中1所在的最高位

for j = 所有数

if i != j && j中1所在的最高位也为p

a[j] = a[j] ^ a[i];

}

其中内层a[j]改了, 外层循环时也会改变。

为了便于把都是0的行给去掉,一开始我们不要把数拆分成二进制,而是预处理一个数组,数组的第k个数表示2^k次方。倒着循环,如果i大于等于的第一个 ak,那么最高位就在第k个位置。是不是在p,用&,根据与运算的特点考虑。如果不用&,就会TLE。

把a从大到小排序,去掉0后,如果还有num个数,num<n,表示原来的数可以异或得到0,因为有重复的。这里需要特判。所以第k大的数相对来说就是第k-1大的数了。

把k拆分成二进制,把是1的位对应的a累积异或。这个是找规律的,为了不越异或越小,所以之前化成行最简式,把最高位的1都错开。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 10005
typedef unsigned long long  LL;
using namespace std;
int T, n, q, k, cnt, tmp, t, num;
LL  sum, s;
LL a
, p
;
void Gauss()
{
for (int i = 1; i <= n; i++)
{
for (int j = 63; j >= 0; j--)
if (a[i]&p[j])
{
tmp = j;
break;
}
for (int j = 1; j <= n; j++)
if (j != i)
{
if (a[j] & p[tmp]) a[j] = a[j] ^ a[i];
}
}
sort(a+1, a+n+1);
t = 1;
while (a[t] == 0 && t <= n) t++;
num = n-t+1;
}
int main()
{
scanf("%d", &T);
p[0] = 1;
for (int i = 1; i <= 63; i++) p[i] = 2 * p[i-1];
int w = 0;
while (T--)
{
w++;
printf("Case #%d:\n", w);
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%llu", &a[i]);
Gauss();
scanf("%d", &q);
for (int i = 1; i <= q; i++)
{
scanf("%d", &k);
if (num < n) k--;
if (k > p[num]-1 )
{
printf("-1\n");
continue;
}
cnt = 0;
sum = 0;
while (k > 0)
{
cnt++;
if (k & 1) sum = sum ^ a[cnt+t-1];
k = k / 2;
}
printf("%llu\n", sum);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: