您的位置:首页 > 其它

[BZOJ2844]albus就是要第一个出场(线性基)

2017-05-06 17:50 218 查看

=== ===

这里放传送门

=== ===

题解

把给出的数字看成二进制,它们都可以看作是向量。然后要求的就是所有向量的线性组合中Q这个向量的出现位置。

首先把给出的所有东西搞一个线性基出来,因为这些基向量的不同组合就能组合出不同的数字。

先不考虑重复数字的问题,如果要求数字Q的出现位置的话,首先一个比较直观的想法是,如果能求出这样一组基底,它从小到大排序以后可以满足:设只选i向量构造出来的数字为R,那么1..i-1的这些向量不管怎么选,构造出来的数字都是小于R的。那么如果把第i个向量的选择状态表示成0或者1,显然1号位置权重最低,n号位置权重最高。

把这组基底中表示出数字Q需要的向量选择状态用01串表示,从n到1读出来的这个二进制数字就是Q的出现位置,因为这个数字的含义实际上就是它前面有多少数字小于等于它。

上面那段话说的可能有点乱o_o。。具体可以类比一下二进制单位向量:{(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)∗2z个数字。

那这样就解决问题啦。。。

下面代码里使用的方法是先计算小于等于Q-1的数字数量,再加上1就得到了Q的第一个出现位置。

众:好TM啰嗦啊你!

ATP:T_T原谅窝的语文水平吧(逃

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const long long Mod=10086;
int n,v[100010],b[40],a[100010],q,tot,now,zero;
long long ans,mul[100010];
int comp(int x,int y){return x>y;}
void Gauss_Eli(){
for (int i=1;i<=n;i++){
for (int j=31;j>=0;j--)
if ((v[i]>>j)&1)
if (b[j]==0){b[j]=i;break;}
else v[i]^=v[b[j]];
if (v[i]==0) ++zero;
}
for (int i=31;i>=0;i--)
for (int j=1;j<=n;j++)
if (b[i]!=j&&((v[j]>>i)&1))
v[j]^=v[b[i]];
sort(v+1,v+n+1,comp);
tot=n;
while (v[tot]==0) --tot;
}
int main()
{
scanf("%d",&n);mul[0]=1;
for (int i=1;i<=n;i++)
mul[i]=(mul[i-1]*2)%Mod;
for (int i=1;i<=n;i++){
scanf("%d",&v[i]);
a[i]=v[i];
}
Gauss_Eli();ans=1;
scanf("%d",&q);
for (int i=1;i<=tot;i++)
if ((now^v[i])<q){
now^=v[i];
ans=(ans+mul[tot-i])%Mod;;
}
ans=(ans*mul[zero])%Mod;//计算“0”基底
if (q==0) ans=1;
else ans=(ans+1)%Mod;
printf("%I64d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息