题目:
题解:
先不考虑重复数字的问题,如果要求数字Q的出现位置的话,首先一个比较直观的想法是,如果能求出这样一组基底,它从小到大排序以后可以满足:设只选i向量构造出来的数字为R,那么1..i-1的这些向量不管怎么选,构造出来的数字都是小于R的。
把这组基底中表示出数字Q需要的向量选择状态用01串表示,从n到1读出来的这个二进制数字就是Q的出现位置,因为这个数字的含义实际上就是它前面有多少数字小于等于它。
举个栗子,二进制单位向量:{(1,0,0,0…0),(0,1,0,0,…,0),(0,0,1,0,…,0),{0,0,0,0,…,1}},这组向量中每个向量看成数字分别是{1,2,4,8,…},可以发现1..i-1号位置的数字就算全部选上,构造出来的向量也是小于第i号位置的数字的。这样的话如果要求数字5的出现位置,可以发现它要用1号向量:(0,0,1)和4号向量:(1,0,0),那5的出现位置就是第5个啦。。。
再举个例子来说,如果是{5,6,8}这三个数字,它可以求出来一组线性基是{3,6,8}。但是这组基底显然不满足我们的要求,因为如果选择了1号和2号向量,它的二进制数表示是3,得到的数字是5,但是它还不如只选2号向量得到的数字6大,那么我们肯定不能说数字5出现的位置是3。5出现的位置明明是2啊对吧。
出现这种问题的原因就是有一个二进制位在两个向量中出现了,那么我们只要把向量组化成行最简型就能解决问题了。
但是如果考虑重复数字的话怎么办呢?
可以发现在求线性基的过程中有一些向量被异或成了0,也就是说它们可以由别的向量线性表示。因为基向量组和原向量组是等价的,那么我们现在来观察基向量组。它里面有很多0向量呀,那也就是异或出来一个数字可以随便加上这些0,值仍然是不变的。
也就是说当我们用上面所说的不考虑重复数字的方法得到的数字Q的出现位置为pos,而现在基向量组里面有z个0向量。那么Q前面的pos-1个数字随便选几个0加进去,得到的数字还是比Q小。那也就是说当考虑重复数字的时候,Q前面一共有(pos−1)∗2^z个数字。
那这样就解决问题啦。。。
下面代码里使用的方法是先计算小于等于Q-1的数字数量,再加上1就得到了Q的第一个出现位置。
引自ATP学姐
注意一个左移的问题,对拍了一会发现不要轻易用<<>>,预处理或者快速幂比较靠谱
代码:
#include <cstdio>
using namespace std;
const int mod=10086;
int a[100005],b[70],p[70];long long num;
int ksm(int a,int k)
{
int ans=1;
for (;k;k>>=1,a=a*a%mod)
if (k&1) ans=ans*a%mod;
return ans;
}
int main()
{
int n,cnt=0,zero=0;scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++)
for (int j=28;j>=0;j--)
if (a[i]>>j&1)
{
if (!b[j]) {b[j]=a[i]; break;}
else a[i]^=b[j];
if (!a[i]) zero++;
}
for (int i=0;i<=28;i++)
for (int j=i-1;j>=0;j--)
if (b[i]>>j&1) b[i]^=b[j];
for (int i=0;i<=28;i++)
if (b[i]) p[cnt++]=b[i];
int w;long long ans=1;scanf("%d",&w);
if (w==0) {printf("1");return 0;}
for (int i=cnt-1;i>=0;i--)
if ((num^p[i])<w) num^=p[i],ans=(ans+ksm(2,i))%mod;
ans=ans*ksm(2,zero)%mod;
printf("%lld",(ans+1)%mod);
}