bzoj 4810: [Ynoi2017]由乃的玉米田 (莫队+bitset)

题目描述

传送门

题目大意:给你一个序列a,长度为n,有m次操作,每次询问一个区间是否可以选出两个数它们的差为x,或者询问一个区间是
否可以选出两个数它们的和为x,或者询问一个区间是否可以选出两个数它们的乘积为x

题解

刚开始想用线段树+bitset,但是发现会MLE。
所以就直接上莫队了。
对于 ab=x ,我们可以对x进行 O(n) 因数分解,判断一下。
对于 ab=x ,bitset中维护数a是否出现过,然后判断右移x后与原bitset按位and,如果有是1的位置,则可行
a+b=x ,一个bitset维护a,一个维护mx-a,然后令mx-x的那个右移(mx-x)与维护a的取and,如果有1的位置,则可行。

代码

#include
#include
#include
#include
#include
#include
#define N 100003
using namespace std;
bitset p,T,d;
int belong[N],a[N],cnt[N],cnt1[N],n,m,mx,ans[N];
struct data{
    int id,x,l,r,opt;
}q[N];
int cmp(data a,data b){
    return belong[a.l]int x,int v)
{
    cnt[x]+=v;
    if (cnt[x]==0&&v==-1) p[x]=0,d[mx-x]=0;
    if (cnt[x]==1&&v==1) p[x]=1,d[mx-x]=1;
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]),mx=max(mx,a[i]);
    for (int i=1;i<=n;i++) scanf("%d%d%d%d",&q[i].opt,&q[i].l,&q[i].r,&q[i].x),q[i].id=i,mx=max(mx,q[i].x);
    int blocksize=sqrt(n);
    for (int i=1;i<=m;i++) belong[i]=(i-1)/blocksize+1;
    int l=1;int r=1;
    change(a[1],1);
    sort(q+1,q+m+1,cmp);
    for (int i=1;i<=m;i++) {
        //cout<<q[i].l<<" "<<q[i].r<<" "<<q[i].x<while (l<q[i].l) change(a[l++],-1);
        while (l>q[i].l) change(a[--l],1);
        while (r>q[i].r) change(a[r--],-1);
        while (r<q[i].r) change(a[++r],1);
        int t=q[i].id;
        if (q[i].opt==3) {
            for (int j=1;j*j<=q[i].x;j++)
             if (q[i].x%j==0)
              if (cnt[j]>0&&cnt[q[i].x/j]>0) {
                ans[t]=1;
                break;
              }
        }
        if (q[i].opt==1) {
            T=p;
            T>>=q[i].x;
            T=T&p;
            if (T.count()) ans[t]=1;
        }
        if (q[i].opt==2) {
            T=d;
            T>>=(mx-q[i].x);
            T=T&p;
            if (T.count()) ans[t]=1;
        }
    }
    for (int i=1;i<=m;i++) 
     if (ans[i]) printf("yuno\n");
     else printf("yumi\n");
}

你可能感兴趣的:(莫队,STL)