P3674 小清新人渣的本愿(莫队+bitset)

目录

Description

给你一个序列 aa,长度为 nn,有 mm 次操作,每次询问一个区间是否可以选出两个数它们的差为 xx,或者询问一个区间是否可以选出两个数它们的和为 xx,或者询问一个区间是否可以选出两个数它们的乘积为 xx ,这三个操作分别为操作 1,2,31,2,3

选出的这两个数可以是同一个位置的数。

State

\(n, m, max(x, a[i])<=10^5\)
\(a[i]>=0, x >= 2\)

Input

​ 5 5
1 1 2 3 4
2 1 1 2
1 1 2 2
3 1 1 1
3 5 5 16
1 2 3 4

Output

​ hana
bi
hana
hana
bi

Solution

看到题目的第一眼感觉用一个数组觉可以解决,但是题目设计的比较妙


如果上一次是 \(1\) 操作,当前为 \(2\) 操作,区间都相同,那么该怎么办呢

这提示我们需要用一种复杂度极低的查询方式,可以利用 \(bitset\) 的与操作

  1. \(bitset & (bitset << x)\) 查找新的集合中是否存在 \(1\)
  2. \(a+b=x\) 其中 \(a,b\) 是在 \(bitset\) 中出现过的数,将起转化为\(N-a-b=N-x\),也就是 \((N-b)-a=(N-x)\),这样又变成了 \(1\) 的形式
  3. 这个就比较套路了,枚举 \(x\) 的因子就可以了

Code

const int N = 1e5 + 5;
 
    int n, m;
    int a[N];
    struct Query
    {
        int l, r;
        int bel;
        int id, opt, x;
        bool operator<(Query o){
            return bel == o.bel ? r < o.r: l < o.l;
        }
        void read(){ 
            sd(opt), sdd(l, r), sd(x); 
        }
    }q[N];
    int block, vis[N];
    bool ans[N];
    bitset<N> now, rev;

void mo()
{
    block = 3 * sqrt(n);
    for(int i = 1; i <= m; i ++){
        q[i].id = i;
        q[i].bel = q[i].l / block + 1;
    }
    sort(q + 1, q + 1 + m);
}

void add(int pos)
{
    int x = a[pos];
    vis[x] ++;
    now[x] = 1;
    rev[N - x] = 1; 
}

void del(int pos)
{
    int x = a[pos];
    vis[x] --;
    if(vis[x]) return ;
    now[x] = 0;
    rev[N - x] = 0;
}

signed main()
{
    while(~ sdd(n, m)){
        rep(i, 1, n) sd(a[i]);
        rep(i, 1, m) q[i].read();
        mo();
        int l = 1, r = 0;
        now = 0;
        rev = 0;
        for(int i = 1; i <= m; i ++){
            while(l > q[i].l) add(-- l);
            while(r < q[i].r) add(++ r);
            while(l < q[i].l) del(l ++);
            while(r > q[i].r) del(r --);
            // dbg(now[1]);
            if(q[i].opt == 1){
                ans[q[i].id] = ((now & (now << q[i].x))).any();
            }
            else if(q[i].opt == 2){
                ans[q[i].id] = ((rev & (now << N - q[i].x)).any());
            }
            else{
                for(int k = 1; k * k <= q[i].x; k ++){
                    if(q[i].x % k == 0 && now[k] && now[q[i].x / k]){
                        ans[q[i].id] = 1;
                        break;
                    }
                }
            }
        }
        rep(i, 1, m) puts(ans[i]? "hana": "bi");
    }
    return 0;
}
posted @ 2021-11-18 10:34  Bcoi  阅读(52)  评论(0)    收藏  举报