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

题目:

我是超链接

题解:

先不考虑重复数字的问题,如果要求数字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);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值