HDU 3949 XOR(线性基)

题目链接
题意

求异或第k小的值

思路

打CF遇到几次,一直没学,终于给看了。
线性基,大概就是用一组数表示异或值域 与 原来一堆数的异或值域相同
学了线代大概就是用数的二进制当作向量,组成行阶梯矩阵。

求第k小,先将线性基转 行最简矩阵,然后根据k的二进制值确定选了哪些。


表达能力不行,有点难说清,用组例子表示

{ 1 0 0 1 0 x 3 0 0 1 0 0 x 2 0 0 0 0 1 x 1 } \left\{ \begin{matrix} 1&0&0&1&0&x3\\ 0&0&1&0&0&x2\\ 0&0&0&0&1&x1\\ \end{matrix} \right\} 100000010100001x3x2x1

值域从小到大
{ 0 0 0 0 1 x 1 0 0 1 0 0 x 2 0 0 1 0 1 x 1 、 x 2 1 0 0 1 0 x 3 1 0 0 1 0 x 3 、 x 1 1 0 0 1 0 x 3 、 x 2 1 0 0 1 1 x 3 、 x 2 、 x 1 } \left\{ \begin{matrix} 0&0&0&0&1&x1\\ 0&0&1&0&0&x2\\ 0&0&1&0&1&x1、x2\\ 1&0&0&1&0&x3\\ 1&0&0&1&0&x3、x1\\ 1&0&0&1&0&x3、x2\\ 1&0&0&1&1&x3、x2、x1\\ \end{matrix} \right\} 00011110000000011000000011111010001x1x2x1x2x3x3x1x3x2x3x2x1
显然 x i {x_i} xi > ( x i − 1 + x i − 2 … x 1 ) (x_{i-1}+x_{i-2}…x_{1}) (xi1+xi2x1),这个性质与二进制相同(本来就是二进制拆分,略有不同) ,所以第k小,将k拆分二进制根据其选数即可。

做法
预处理行最简矩阵得到新数组 v 表示第i-1大的向量。
注意是否能异或出0,矩阵行的数量 == 给你的元素数量,则不可以。显然证明略。
存在0,k -= 1。

代码
#include <bits/stdc++.h>
using namespace std;

#define ll long long

ll p[100], v[100];

int main()
{
    ll t, ca = 1;
    for(scanf("%lld",&t); ca <= t; ++ca)
    {
        memset(p,0,sizeof(p));
        memset(v,0,sizeof(v));
        ll n, tmp;
        scanf("%lld",&n);
        for(ll i = 0; i < n; ++i) // 行阶梯矩阵
        {
            scanf("%lld",&tmp);
            for(ll j = 63; ~j; --j)
            {
                if((tmp>>j)&1)
                {
                    if(p[j]) tmp ^= p[j];
                    else
                    {
                        p[j] = tmp;
                        break;
                    }
                }
            }
        }
        for(ll i = 63; ~i; --i) // 行最简型矩阵
            for(ll j = i+1; j <= 63; ++j)
                if((p[j] >> i) & 1) p[j] ^= p[i];
        ll num = 0, q, k;
        for(ll i = 0; i < 64; ++i) if(p[i]) v[num++] = p[i];
        scanf("%lld",&q);
        printf("Case #%lld:\n",ca);
        while(q--)
        {
            scanf("%lld",&k);
            if(n != num) --k;
            if((1ll<<num)-1 < k) // 1ll 忘记开ll 沙雕bug
            {
                printf("-1\n");
                continue;
            }
            ll ans = 0;
            for(ll i = 0; i < 64; ++i) if((k>>i)&1) ans ^= v[i];
            printf("%lld\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值