[Luogu3674]小清新人渣的本愿

luogu

题意

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

sol

正好今天考试一道题要用到\(bitset\)就跑过来写一下。
所谓\(bitset\)其实就是一个不用手写的压位,一般用来优化暴力,复杂度\(O(\frac{n^2}{64})\)哈。(毕竟很多时候除个\(64\)复杂度就可以过了)
这个题哈。用莫队的方法离线处理每个询问,把每种数字出现的集合压进一个\(bitset\)。对于减法就直接用SS>>x取交集判断是否为空。对于加法我们需要额外维护一个反过来的\(bitset\),然后也是右移一下然后取个交。
对于乘法,可以直接枚举因数判断是否存在即可。
复杂度\(O(\frac{n^2}{64}+n\sqrt n+m\sqrt n)\),所以说这是一个正确的复杂度。

莫队可以加一些优化,比如说对右端点排序的时候根据左端点所在块的奇偶性从大到小或者是从小到大排序。(还是快了蛮多的)

code

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<bitset>
using namespace std;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e5;
int n,m,blk,a[N+5],cnt[N+5],ans[N+5];
struct query{
    int opt,l,r,x,id;
    bool operator < (const query &b) const
        {
            if (l/blk!=b.l/blk) return l/blk<b.l/blk;
            return ((l/blk)&1)?r>b.r:r<b.r;
        }
}q[N+5];
bitset<N+5>S1,S2;
void add(int x)
{
    ++cnt[x];
    if (cnt[x]==1) S1[x]=1,S2[N-x]=1;
}
void del(int x)
{
    --cnt[x];
    if (cnt[x]==0) S1[x]=0,S2[N-x]=0;
}
int main()
{
    n=gi();m=gi();blk=sqrt(n);
    for (int i=1;i<=n;++i) a[i]=gi();
    for (int i=1;i<=m;++i) q[i]=(query){gi(),gi(),gi(),gi(),i};
    sort(q+1,q+m+1);
    int L=1,R=0;
    for (int i=1;i<=m;++i)
    {
        while (R<q[i].r) add(a[++R]);
        while (L>q[i].l) add(a[--L]);
        while (R>q[i].r) del(a[R--]);
        while (L<q[i].l) del(a[L++]);
        if (q[i].opt==1)
            ans[q[i].id]=(S1&(S1>>q[i].x)).any();
        if (q[i].opt==2)
            ans[q[i].id]=(S1&(S2>>N-q[i].x)).any();
        if (q[i].opt==3)
            for (int j=1;j*j<=q[i].x;++j)
                if (q[i].x%j==0)
                    if (S1[j]&&S1[q[i].x/j]) {ans[q[i].id]=1;break;}
    }
    for (int i=1;i<=m;++i) puts(ans[i]?"hana":"bi");
    return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/8782828.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值