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

15 篇文章 0 订阅

题目描述

传送门

题目大意:给你一个序列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<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<bitset>
#define N 100003
using namespace std;
bitset<N> 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]<belong[b.l]||belong[a.l]==belong[b.l]&&a.r<b.r;
}
void change(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<<endl;
        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");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值