Description
已知一个长度为n的正整数序列A(下标从1开始), 令 S = { x | 1 <= x <= n }, S 的幂集2S定义为S 所有子
集构成的集合。定义映射 f : 2S ->
Z
f
Z_f
Zf(空集) = 0f(T) = XOR A[t] , 对于一切t属于T,现在albus把2S中每个集合的f值计算出来, 从小到大排成一行, 记为序列B(下标从1开始)。 给定一个数, 那么这个数在序列B中第1次出现时的下标是多少呢?
Input
第一行一个数n, 为序列A的长度。接下来一行n个数, 为序列A, 用空格隔开。最后一个数Q, 为给定的数.
Output
共一行, 一个整数, 为Q在序列B中第一次出现时的下标模10086的值.
大意
给定包含n个元素的数组
a
[
]
a[]
a[],显然
a
[
]
a[]
a[]共有2n个子集(包括空集及其自身),将这2n个子集的异或值从小到大排序,排序后的下标从1开始,求给定的输入Q第一次出现时的下标mod 10086的值。
思路
我们知道对于一个其中有
r
r
r个元素的线性基,任选其中的子集(包括空集)我们能组合出2r个不同的异或值。但是只有这个结论并不够
同时若一个集合内有
n
n
n个元素而其对应的线性基有
r
r
r个元素,那么对于每个可以由线性基中的元素异或出的异或值,在原集合中都有2n-r中选法能够构造出这个异或值。
证明如下:
- 对于任意一个可构造的异或值 x o r i xor_i xori,先从线性基中选取一组元素构造这个异或值(显然选法唯一),再考虑选取不在线性基中的 n − r n-r n−r个元素,则有2n-r种选法,而后,对于每一个不在线性基中的元素 a i a_i ai,均可在线性基中选出对应的一组元素与 a i a_i ai的异或值为0(选法唯一)。而线性基中的一个元素若被选取(异或)了两次则相当于没有被选取,故对于2n-r种选法在线性基中都有唯一对应的选取方法可以构造出目标异或值 x o r i xor_i xori。
所以只需要计算出线性基中的元素个数
r
r
r,再找出Q是线性基中第
k
k
k小的可构造异或值,那么答案就是
(
2
n
−
r
∗
(
k
−
1
)
+
1
)
m
o
d
10086
(2^{n-r}*(k-1)+1) mod 10086
(2n−r∗(k−1)+1)mod10086了。
但是不用快速幂可能会TLE
考虑可以选取空集,所以0也是可构造的异或值
AC代码
之前数组开小了一直RE
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<stdlib.h>
#include<string.h>
#define MOD 10086
using namespace std;
typedef long long ll;
typedef struct L_B
{
#define NUM 35
private:
ll b[NUM+5],p[NUM+5],flag,cnt;
public:
L_B()
{
for(int i=0;i<NUM+5;i++)b[i]=p[i]=0;cnt=0;flag=1;
}
void insert(ll now)//插入
{
for(int i=NUM;i>=0;--i)
{
if(now&(1ll<<i))
if(this->b[i])now^=this->b[i];
else
{
this->b[i]=now;return;
}
}
this->flag=1;
return;
}
ll kth(ll k)//取第k大
{
if(this->flag)--k;
if(!k)return 0;
ll ret=0;
if(k>=(1ll<<this->cnt))return -1;
for(int i=0;i<=this->cnt-1;++i)
if(k&(1ll<<i))
ret^=p[i];
return ret;
}
void rebuild()//重构
{
for(int i=1;i<=NUM;i++)
if(this->b[i])
{
for(int j=0;j<i;j++)
{
if(this->b[i]&(1ll<<j))
this->b[i]^=this->b[j];
}
}
for(int i=0;i<=NUM;i++)
{
if(this->b[i])p[(this->cnt)++]=this->b[i];
}
return;
}
ll get_max(ll now)//取最大值
{
ll ret=now;
for(int i=NUM;i+1;--i)
{
if((ret^this->b[i])>ret)ret^=this->b[i];
}
return ret;
}
ll get_min()//取最小值
{
if(this->flag)
return 0;
for(int i=0;i<=NUM;i++)
{
if(this->b[i])return this->b[i];
}
return 0;
}
void merge(L_B n2)//合并线性基
{
for(int i = 0;i <= NUM;++i)
if(n2.b[i])
this->insert(n2.b[i]);
this->flag=this->flag|n2.flag;
return;
}
ll find(ll target)
{
ll ans=0;
for(ll i=cnt;i+1;i--)
{
if((target^p[i])<target)
{
//target^=p[i];
ans+=1ll<<i;
}
}
return ans+this->flag;
}
ll get_cnt(){return this->cnt;}
#undef NUM
} L_B;
long long fast_pow(long long target,long long p);
L_B B;
ll n,q,qu,t,a[100005],Case=0,k;
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)B.insert(a[i]);
B.rebuild();
scanf("%lld",&k);
ll t=fast_pow(2,n-B.get_cnt());
ll ans=B.find(k)-1;
ans*=t;ans++;ans%=MOD;
printf("%lld\n",ans);
return 0;
}
long long fast_pow(long long target,long long p)
{
long long z[50];
z[1]=target;
for(int i=2;i<50;i++)
{
z[i]=z[i-1]*z[i-1];z[i]%=MOD;
}
long long ans=1;
for(int i=1;p;i++)
{
if(p%2) ans=(ans*z[i])%MOD;p/=2;
}
return ans;
}