【BZOJ 4810】【YNOI 2017】由乃的玉米田【莫队+bitset】

本文介绍了一种利用Bitset优化区间查询问题的方法,通过维护数值出现情况,实现了对序列中元素差、和及乘积的高效查询。适用于莫队算法场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给你一个序列a,长度为n,有m次操作,每次询问一个区间是否可以选出两个数它们的差为x,或者询问一个区间是否可以选出两个数它们的和为x,或者询问一个区间是否可以选出两个数它们的乘积为x ,这三个操作分别为操作1,2,3选出的这两个数可以是同一个位置的数

Solution

  一开始直接想到的是莫队,但莫队归莫队,怎么处理这三个操作呢?看看了大神题解,又听了一番同市神犇的指点,终于学会了一点 bitset 的应用。
  
  对 bitset 的学习,可以参考这篇:传送
  
  用 bitset 维护每个值是否出现,再用一个 s 数组来保存这个值出现了几次,这样维护一下就好了。
  
  乘法操作,直接通过n12的方法判断即可。
  
  减法操作,相当于是查询是否存在ii,jj满足 a[i]a[j]=x 的形式,那么把 bitset 右移x位与原来的&一下看新的二进制数是否为0(用 bitset 自带的 count 就好了)
  
  加法类似,不过需要维护的是 a[i]+a[j]=x 的形式,那么我用 c c是所有数中的最大值)去减 a[j] ,即变成 a[i]=(ca[j])+xc 的形式,另外维护一个 bitset ,右移 (cx) 位查询就好了。
  复杂度 O(n32+n232)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<cmath>
#include<iostream>

using namespace std;
const int N = 100010;
bitset<N> f,g;

int a[N],ans[N],pos[N],s[N];
int n,m;
struct node{int id,o,l,r,x;}qu[N];
inline bool cmp(node a,node b) {
    return pos[a.l] == pos[b.l] ? a.r < b.r : pos[a.l] < pos[b.l];
}

int main()
{
    scanf("%d%d",&n,&m);
    int bs = (int)sqrt(n);
    for(int i = 1;i <= n;i++) {
        scanf("%d",&a[i]);
        pos[i] = (i-1)/bs;
    }
    for(int i = 1;i <= m;i++) {
        scanf("%d%d%d%d",&qu[i].o,&qu[i].l,&qu[i].r,&qu[i].x);
        qu[i].id = i;
    }
    sort(qu+1,qu+m+1,cmp);

    int pl = 1,pr = 0;
    memset(s,0,sizeof(s));
    for(int i = 1;i <= m;i++)
    {
        if(qu[i].l == qu[i].r){ans[qu[i].id] = 0;continue;}
        if(pr < qu[i].r)
            for(int j = pr+1;j <= qu[i].r;j++) {
                s[a[j]]++; f[a[j]]=1; g[N-a[j]]=1;
            }
        else
            for(int j = pr;j > qu[i].r;j--) {
                s[a[j]]--;
                if(!s[a[j]]){f[a[j]]=0;g[N-a[j]]=0;}
            }
        pr = qu[i].r;
        if(pl < qu[i].l)
            for(int j = pl;j < qu[i].l;j++) {
                s[a[j]]--;
                if(!s[a[j]]){f[a[j]]=0;g[N-a[j]]=0;}
            }
        else
            for(int j = pl-1;j >= qu[i].l;j--) {
                s[a[j]]++; f[a[j]]=1; g[N-a[j]]=1;
            }
        pl = qu[i].l;

        if(qu[i].o == 1)
            ans[qu[i].id] = ((f>>qu[i].x)&f).any();
        if(qu[i].o == 2)
            ans[qu[i].id] = ((g>>(N-qu[i].x))&f).any();
        if(qu[i].o == 3) {
            if(qu[i].x == 0 && f[0])
                ans[qu[i].id] = 1;
            for(int j = 1;j*j<=qu[i].x;j++)
                if(!(qu[i].x%j))
                    if(f[j]&f[qu[i].x/j]) {
                        ans[qu[i].id] = 1;
                        break;
                    }
        }
    }
    for(int i = 1;i <= m;i++)
        printf(ans[i] ? "yuno\n" : "yumi\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值