https://www.lydsy.com/JudgeOnline/problem.php?id=2844
已知一个长度为n的正整数序列A(下标从1开始), 令 S = { x | 1 <= x <= n }, S 的幂集2^S定义为S 所有子
集构成的集合。定义映射 f : 2^S -> Zf(空集) = 0f(T) = XOR A[t] , 对于一切t属于T现在albus把2^S中每个集
合的f值计算出来, 从小到大排成一行, 记为序列B(下标从1开始)。 给定一个数, 那么这个数在序列B中第1
次出现时的下标是多少呢?
Input
第一行一个数n, 为序列A的长度。接下来一行n个数, 为序列A, 用空格隔开。最后一个数Q, 为给定的数.
Output
共一行, 一个整数, 为Q在序列B中第一次出现时的下标模10086的值.
Sample Input
3 1 2 3 1
Sample Output
3 样例解释: N = 3, A = [1 2 3] S = {1, 2, 3} 2^S = {空, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}} f(空) = 0 f({1}) = 1 f({2}) = 2 f({3}) = 3 f({1, 2}) = 1 xor 2 = 3 f({1, 3}) = 1 xor 3 = 2 f({2, 3}) = 2 xor 3 = 1 f({1, 2, 3}) = 0 所以 B = [0, 0, 1, 1, 2, 2, 3, 3]
Hint
数据范围:
1 <= N <= 10,0000
其他所有输入均不超过10^9
思路:跟这道题学了一个新操作:查一个值是异或第几小。其实也就是查异或第k小的逆过程。然后这道题其实是有规律的,但是我不会证明。证明可以看这个博客:
https://blog.sengxian.com/algorithms/linear-basis
这里只说一下结论:
每个数出现的次数都相等,每个数出现的次数是: 2 n − ∣ B ∣ 其 中 B 是 输 入 的 数 的 一 个 线 性 基 2^{n-|\mathfrak{B}|}\quad其中\mathfrak{B}是输入的数的一个线性基 2n−∣B∣其中B是输入的数的一个线性基
#include<bits/stdc++.h>
#define ll long long
#define MAXN 10005
#define MOD 10086
using namespace std;
int t,n,q;
ll k,tmp;
struct L_B
{
ll b[65],p[65],gx[65],base[65];
int cnt,flag;
L_B()
{
memset(gx,0,sizeof(gx));
memset(p,0,sizeof(p));
memset(b,0,sizeof(b));
cnt=flag=0;
for(int i=0;i<=62;i++)
base[i]=1ll<<i;
}
inline bool insert(ll x)//向线性基中插入元素
{
for(int i=62;i>=0;--i)
if(x&base[i])
{
if(b[i])
x^=b[i];
else
{
b[i]=x;
return true;
}
}
flag=1;//可异或得到0
return false;
}
ll get_max()//求最大异或值
{
ll ret=0;
for(int i=62;i>=0;--i)
if((ret^b[i])>ret)
ret^=b[i];
return ret;
}
ll get_min()//求最小异或值
{
if(flag)
return 0;
for(int i=0;i<=62;++i)
if(b[i])
return b[i];
return 0;
}
inline void rebuild()//重构 找第k小的前置步骤
{
for(int i=62;i>=1;--i)
if(b[i])
for(int j=i-1;j>=0;--j)
if(b[i]&base[j])
b[i]^=b[j];
for(int i=0;i<=62;++i)
if(b[i])
p[cnt++]=b[i],gx[cnt-1]=i;
}
ll kth(ll k)//找第k小
{
if(flag)
--k;
if(k==0)
return 0;
ll ret=0;
if(k>=base[cnt])
return -1;
for(int i=0;i<=cnt-1;++i)
if(k&base[i])
ret^=p[i];
return ret;
}
ll getindex(ll v)//看v是异或和第几小
{
ll ans=0;
if(flag)
++ans;
for(int i=cnt-1;i>=0;i--)
if(v&base[gx[i]])
ans+=base[i];
return ans;
}
void merge(L_B &n2)//把线性基n2 内的元素 逐一插入到线性基n1 中
{
flag|=n2.flag;
for(int i=0;i<=62;++i)
if(n2.b[i])
insert(n2.b[i]);
}
};
ll qpow(ll a,ll b)
{
ll t1=1,t2=a;
while(b)
{
if(b&1)
t1=t1*t2%MOD;
t2=t2*t2%MOD;
b>>=1;
}
return t1;
}
int main()
{
L_B lis;
int n;
scanf("%d",&n);
ll tmp;
for(int i=0;i<n;i++)
{
scanf("%lld",&tmp);
lis.insert(tmp);
}
lis.rebuild();
scanf("%lld",&tmp);
ll ans=lis.getindex(tmp);
printf("%lld\n",((ans-1)%MOD*qpow(2,n-lis.cnt)%MOD+1)%MOD);
return 0;
}