题目链接
题意
求异或第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\}
⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧00011110000000011000000011111010001x1x2x1、x2x3x3、x1x3、x2x3、x2、x1⎭⎪⎪⎪⎪⎪⎪⎪⎪⎬⎪⎪⎪⎪⎪⎪⎪⎪⎫
显然
x
i
{x_i}
xi >
(
x
i
−
1
+
x
i
−
2
…
x
1
)
(x_{i-1}+x_{i-2}…x_{1})
(xi−1+xi−2…x1),这个性质与二进制相同(本来就是二进制拆分,略有不同) ,所以第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;
}