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

该博客探讨了如何利用高斯消元法和XOR线性基解决HDOJ 3949题目中的异或问题。首先将数的二进制表示排列并转换为行最简形式,找到极大无关组。然后通过特定算法处理数组,消除零行,并处理特殊情况。最后,根据二进制拆分计算第k大的异或结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


点击打开链接


题意:给出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[N], p[N];
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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值