分析:
这道题是hdu3949的逆运算(可能题面有点难懂)
既然我们能够知道排名是第k的数是多少
就可以直接二分,变成判定性问题
当然,这道题还有其他解法:
因为是子集异或和,可以考虑用线性基解决
把每一个数看成是一个向量,求出线性基
线性基是一个极小集:能够张成原空间的最小线性不相关向量集
那么任何一个异或和都可以用线性基得到
但是题目中会计算重复的数值,而线性基是不会计算重复元素的
我们先不考虑重复数字的问题
我们先回顾一下hdu3949的思路:
把k二进制拆分,如果k的第i位上是1,ans^=b[i]
虽然是^=b[i],然而由于矩阵是对角线型的,实际上就等价于ans+=b[i]
我们可以通过上面的过程得到ta的逆算法:
一共有
n
n
个数字,得到了个线性基
注意:要使用此方法,要把矩阵消成对角线式
我们举一个简单点的例子解释一下算法:
n=3
a: 3 4 7
我们得到的线性基是这样的:
i 2 1 0
B[2] 1 0 0 =4
B[1] 0 1 0 =2
B[0] 0 0 1 =1
现在我们要查找
X=5
X
=
5
的排名
k
k
我们先想想,已知的情况下,如何得到
X
X
:
显然,把二进制分解后,如果第
i
i
位是1,那么
那么我们先把
X
X
二进制分解了:
我们找到
B[x]=22,B[y]=20
B
[
x
]
=
2
2
,
B
[
y
]
=
2
0
的
x,y
x
,
y
k=2x+2y
k
=
2
x
+
2
y
这里我们需要解释一下以上所说的B的含义:去除0后的b数组
例如:
b={1,2,0,0,16,0,64}
B={1,2,16,64}
b和B不是一个回事!!!
之后我们就可以考虑重复的问题了:
- 如果一个元素能够插入到在线性基中,则说明ta一定能为线性基贡献一个1
- 如果一个元素不能插入线性基,则说明ta一定能用若干个线性基组合而成
设我们得到线性基的大小为
k
k
则一共有个数对线性基没有贡献
那么这些数加入之后对异或和没有影响:可以直接视为0
对于任意一个数
i
i
的线性组合,我们可以随便选几个0加进去
那么的重复次数就是:
2n−k
2
n
−
k
因为可以有空集,所以0肯定会出现
我们可以先计算小于等于 X−1 X − 1 的数字数量,加上1就得到了 X X 的第一个出现位置
但是在代码实现中:
scanf("%d",&x);
int ans=0;
int tmp=cnt;
for (int i=31;i>=0;i--)
if (b[i]) {
if (x>>i&1) {
ans=ans+(1LL<<(tmp-1))*e[n-cnt]%p;
ans%=p;
}
tmp--;
}
ans++; ans%=p;
按理来说,应该在一开始:x- -
但是减掉1之后,将X二进制分解完,最后还要加上0的重复次数
所以我们就不用麻烦这一步啦
tip
不要忘了取模
WA了几次
问题就在于这个重复次数:
2n−k
2
n
−
k
n-k是可以高达1e5,所以我们只能在一开始预处理出
2i(mod)p
2
i
(
m
o
d
)
p
的值:
for (int i=1;i<=n;i++) e[i]=(e[i-1]<<1)%p;
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100005;
const int p=10086;
int n,x,a[N],b[100],e[N];
void cal() {
for (int i=1;i<=n;i++)
for (int j=31;j>=0;j--)
if (a[i]>>j&1) {
if (b[j]) a[i]^=b[j];
else {
b[j]=a[i];
for (int k=j-1;k>=0;k--)
if (b[k]&&(b[j]>>k&1)) b[j]^=b[k];
for (int k=j+1;k<=31;k++)
if (b[k]>>j&1) b[k]^=b[j];
break;
}
}
}
int main()
{
scanf("%d",&n);
e[0]=1;
for (int i=1;i<=n;i++) e[i]=(e[i-1]<<1)%p;
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
cal();
int cnt=0;
for (int i=0;i<=31;i++)
if (b[i])
cnt++;
scanf("%d",&x);
int ans=0;
//有空集,0肯定会出现
int tmp=cnt;
for (int i=31;i>=0;i--)
if (b[i]) {
if (x>>i&1) {
ans=ans+(1LL<<(tmp-1))*e[n-cnt]%p;
ans%=p;
}
tmp--;
}
ans++; ans%=p;
printf("%d\n",ans);
return 0;
}