Luogu

分析

考虑莫队,并开两个 bitset,分别存 $x$ 是否在区间内和 $100000-x$ 是否在区间内,假设分别叫 $A$ 和 $B$。

那么对于询问 $-$,直接判断 A&(A<<x) 是否有 $1$ 即可;对于询问 $+$,直接判断 B&(A<<(100000-x)) 是否有 $1$ 即可。这两种询问单次都是 $\mathcal{O}(\frac{n}{\omega})$ 的。

对于询问 $\times$,直接枚举 $x$ 的约数 $d$ 并判断 $d$ 和 $\frac{x}{d}$ 是否都存在即可,单次询问 $\mathcal{O}(\sqrt{n})$。

对于询问 $\div$,一个想法是直接枚举 $d$ 并判断 $d$ 和 $dx$ 是否都存在。然而这个只有在 $x\geq\sqrt{n}$ 时单次询问的复杂度才是 $\mathcal{O}(\sqrt{n})$。

因此我们把所有 $x<\sqrt{n}$ 的询问 $\div$ 单独拿出来求:枚举 $x\in[1,\sqrt{n})$,然后从左往右枚举右端点 $r$,并求出 $lst_t$ 表示 $t$ 上一次出现的位置和 $mx_r$ 表示最大的满足 $[l,r]$ 内能有两个数商为 $x$ 的 $l$(直接用 $lst$ 更新即可),这样子只需要对于右端点是 $r$ 的询问判断它的左端点是否 $\leq mx_r$ 即可。这一部分总复杂度是 $\mathcal{O}(n\sqrt{n})$。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
#pragma GCC optimize("Ofast,unroll-loops,fast-math")
using namespace std;
typedef long long ll;

namespace IO {
    const int SIZE=1<<21;
    char ibuf[SIZE|1],*iS=ibuf,*iT=ibuf,obuf[SIZE|1],*oS=obuf,*oT=obuf+SIZE-1;
    void flush() { fwrite(obuf,1,oS-obuf,stdout); oS=obuf; }
    char getc() {
        if (iS==iT) {
            iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin);
            if (iS==iT) return EOF;
        }
        return *iS++;
    }
    void putc(char x) { *oS++=x; if (oS==oT) flush(); }
    int read() {
        int X=0,w=1; char c=getc();
        while (c<'0'||c>'9') { if (c=='-') w=-1; c=getc(); }
        while (c>='0'&&c<='9') X=X*10+c-'0',c=getc();
        return X*w;
    }
    struct flusher { ~flusher() { flush(); } } io_flusher;
}
using IO::putc;
using IO::read;

const int N=100000+10;

int n,m,Q=0,a[N],B;
bool ans[N];
struct query { int op,l,r,x,id; } q[N];
bool operator <(query a,query b) {
    if (a.l/B!=b.l/B) return a.l<b.l;
    else return (a.l/B)&1?a.r<b.r:a.r>b.r;
}
vector<query> q2[N];

namespace SP {
    int lst[N],mx[N];
    void main() {
        for (int w=1;w<=316;++w) {
            memset(lst,0,sizeof(lst)),memset(mx,0,sizeof(mx));
            if (q2[w].empty()) continue;
            for (int i=1,p=0;i<=n;++i) {
                lst[a[i]]=i;
                if (w*a[i]<=100000) p=max(p,lst[w*a[i]]);
                if (a[i]%w==0) p=max(p,lst[a[i]/w]);
                mx[i]=p;
            }
            for (auto i:q2[w]) ans[i.id]=(i.l<=mx[i.r]);
        }
    }
}

bitset<N> B1,B2; int cnt[N];
void add(int x) {
    if (!cnt[x]) B1[x]=B2[100000-x]=1;
    ++cnt[x];
}
void del(int x) {
    --cnt[x];
    if (!cnt[x]) B1[x]=B2[100000-x]=0;
}

bool Q1(int x) { // -
    return (B1&(B1<<x)).any();
}
bool Q2(int x) { // +
    return (B2&(B1<<(100000-x))).any();
}
bool Q3(int x) { // *
    for (int i=1;i*i<=x;++i)
        if (x%i==0&&B1[i]&&B1[x/i]) return 1;
    return 0;
}
bool Q4(int x) { // /
    for (int i=1;i*x<=100000;++i)
        if (B1[i]&&B1[i*x]) return 1;
    return 0;
}

int main() {
    n=read(),m=read(); B=sqrt(n);
    for (int i=1;i<=n;++i) a[i]=read();
    for (int i=1;i<=m;++i) {
        int op=read(),l=read(),r=read(),x=read();
        if (op==4&&x<=316) q2[x].emplace_back((query){op,l,r,x,i});
        else q[++Q]=(query){op,l,r,x,i};
    }
    SP::main(); sort(q+1,q+Q+1);
    for (int i=1,l=1,r=0;i<=Q;++i) {
        while (l>q[i].l) --l,add(a[l]);
        while (r<q[i].r) ++r,add(a[r]);
        while (l<q[i].l) del(a[l]),++l;
        while (r>q[i].r) del(a[r]),--r;
        if (q[i].op==1) ans[q[i].id]=Q1(q[i].x);
        if (q[i].op==2) ans[q[i].id]=Q2(q[i].x);
        if (q[i].op==3) ans[q[i].id]=Q3(q[i].x);
        if (q[i].op==4) ans[q[i].id]=Q4(q[i].x);
    }
    for (int i=1;i<=m;++i) {
        if (ans[i]) putc('y'),putc('u'),putc('n'),putc('o');
        else putc('y'),putc('u'),putc('m'),putc('i');
        putc('\n');
    }
    return 0;
}
最后修改:2020 年 07 月 02 日 10 : 21 PM