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

 

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=4810

 

【题目大意】

  给出一个数列,有三种区间查询,
  分别查询区间是否存在两个数乘积为x,是否存在两个数和为x,以及是否存在两个数差为x,

 

【题解】

  我们对于询问进行莫队处理,保存当前区间的权值数组,记为F,
  同时保存权值数组的反向数组G
  那么存在差为x的情况只要存在一组F[i]&F[i-x]=1即可
  存在和为x的情况只要存在一组F[i]&G[M-x+i]即可。
  对于乘积为x的情况,我们枚举x的约数,判断F[i]&F[x/i]是否存在。
  复杂度O(nsqrt(n)+nm/w)

 

【代码】

#include <cstdio>
#include <algorithm>
#include <bitset>
#include <cmath>
using namespace std;
const int N=100010,M=100000;
int limit,n,m,pos[N],a[N],ans[N],cnt[N];
bitset<N> F,G;
struct Q{
    int l,r,x,id,op;
    friend bool operator < (const Q &a,const Q &b){
        return pos[a.l]<pos[b.l]||(pos[a.l]==pos[b.l]&&a.r<b.r);
    }
}ask[M];
int read(int &x){
    int f=1;char ch=getchar();x=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
int main(){
    read(n); read(m);
    limit=(int)sqrt(n+0.5);
    for(int i=1;i<=n;i++)read(a[i]),pos[i]=(i-1)/limit+1;
    for(int i=1;i<=m;i++)read(ask[i].op),read(ask[i].l),read(ask[i].r),read(ask[i].x),ask[i].id=i;
    sort(ask+1,ask+m+1);
    for(int i=1,l=1,r=0;i<=m;i++){
        for(;r<ask[i].r;r++)cnt[a[r+1]]++,F.set(a[r+1]),G.set(M-a[r+1]);
        for(;l>ask[i].l;l--)cnt[a[l-1]]++,F.set(a[l-1]),G.set(M-a[l-1]);
        for(;l<ask[i].l;l++){cnt[a[l]]--;if(!cnt[a[l]])F.reset(a[l]),G.reset(M-a[l]);}
        for(;r>ask[i].r;r--){cnt[a[r]]--;if(!cnt[a[r]])F.reset(a[r]),G.reset(M-a[r]);}
        if(ask[i].op==1){if((F&(F>>ask[i].x)).any())ans[ask[i].id]=1;}
        else if(ask[i].op==2){if((F&(G>>(M-ask[i].x))).any())ans[ask[i].id]=1;}
        else{
            for(int j=1;j*j<=ask[i].x;j++)if(ask[i].x%j==0){
                if(F[j]&F[ask[i].x/j]){ans[ask[i].id]=1;break;}
            }
        }
    }for(int i=1;i<=m;i++)puts(ans[i]?"yuno":"yumi");
    return 0;
}
posted @ 2017-07-30 16:48  forever97  阅读(225)  评论(0编辑  收藏  举报