题解:P4869 albus就是要第一个出场
节选自:线性代数学习笔记(二):线性基
为什么没有人写简单易懂的二分答案。
先介绍一下如何在线性基中求第 \(k\) 大 / 小值。我们记 \(w_i\) 表示线性基中二进制位最高位为 \(i\) 的那个基。
我们考虑求第 \(k\) 小的困难在哪里,就是有些 \(w_i\) 会有好几个二进制位相同,异或时会互相干扰,有些时候两个基异或起来会变大,有些时候两个基异或起来会变小。因此不方便统计,如果我们能把基变成如下形式:
(其中 \(x_i\) 也为二进制下的数,加号表示把两个二进制数拼在一起)
我们设最后化简出了 \(n\) 个基 \(w_1 < w_2 < w_3 < \dots < w_{n - 1} < w_n\),此时最小的能被异或出的数就变成了 \(w_1\),第二小是 \(w_2\),第三小是 \(w_1 \operatorname{xor} w_2\)(此时的 \(\operatorname{xor}\) 不会使两个基异或起来变小这),这时我们发现如果将 \(k\) 二进制表示成了 \((b_0b_1b_2\dots b_{x - 1}b_x)\),那么答案就是 \(\operatorname{xor}_{0 \leq i \leq x} 2^i [b_i = 1]\)。
现在的问题就是要求出这样一组性质优良的基。其实我们可以发现,每个 \(w_i\) 都是所有线性基中 \(w_i\) 最小的,根据这个特性,可以贪心地变化这组线性基。
我们从小到大枚举先前求出的基 \(w_i\),再从大到小枚举 \(j > i\),此时若存在 \(w_i \operatorname{xor} w_j < w_i\),就直接异或掉。我们利用类似数学归纳法的方式证明其正确性。考虑到如果 \(w_i \operatorname{xor} w_j < w_j\),那么一定是 \(w_j\) 的某些位与 \(w_i\) 相同,如果 \(w_i\) 的最高位与 \(w_j\) 的这 \(1\) 位都是 \(1\),那么异或就将 \(1\) 消掉了,否则由于 \(w_i \operatorname{xor} (w_i \operatorname{xor} w_j) = w_j\),因此这个线性基并未发生变化,但是此时就只有 \(w_i\) 在 \(i\) 位置有值了,因此我们就构造成功了。
其实,我们可以再简化一下,如果 \(j > i\) 且 \(j\) 的第 \(i\) 位上都有 \(1\) 此时直接将 \(w_j \operatorname{xor} w_i\) 就可以了。
回到这道题目,我们只需要套一个二分答案就可以了,时间复杂度为 \(O(\log^2 D)\)(\(D\) 为值域)。
注意到这道题目是一个多重集合,由于我们知道线性基中,不存在一组数,使他们的异或和为 \(0\),那么可以推出,不存在两组数,使它们的异或和相等。那么,假如有 \(tot\) 个数被插入了线性基中,那么插入失败的数就有 \(n - tot\) 个。由于这些数全部可以用线性基表示出来,因此可以用 \(0\) 来表示。由于多少个 \(0\) 异或上一个数还是这个数,因此其实每个数都出现了 \(2^{n - tot}\) 次,那么将最终答案乘以 \(2^{n - tot}\) 即可。
完整代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 59, MOD = 10086;
int n, m;
int w[N], cnt;
int qpow(int a, int b){
int res = 1;
while(b > 0){
if(b & 1)
res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
struct Basis{
void insert(int x){
for(int i = 55; i >= 0; --i){
if((x >> i) & 1){
if(w[i])
x ^= w[i];
else {
w[i] = x;
break;
}
}
}
}
void build(){
for(int i = 55; i >= 0; i--){
if(!w[i])
continue;
for(int j = i + 1; j <= 55; j++){
if((w[j] >> i) & 1)
w[j] ^= w[i];
}
}
for(int i = 0; i <= 55; i++){
if(w[i])
w[cnt++] = w[i];
}
cnt--;
}
int query(int x){
int res = 0;
for(int i = cnt; i >= 0; i--)
if((x >> i) & 1)
res ^= w[i];
return res;
}
} b;
signed main(){
scanf("%lld", &n);
for(int i = 1; i <= n; i++){
int x;
scanf("%lld", &x);
b.insert(x);
}
b.build();
int x;
scanf("%lld", &x);
int l = 0, r = (1ll << (cnt + 1)) - 1;//枚举排名
while(l < r){
int mid = (l + r + 1) >> 1;
if(b.query(mid) <= x)
l = mid;
else
r = mid - 1;
}
printf("%lld", (qpow(2, n - cnt - 1) * l + 1) % MOD);
return 0;
}
本文来自博客园,作者:Orange_new,转载请注明原文链接:https://www.cnblogs.com/JPGOJCZX/p/18783058

浙公网安备 33010602011771号