BZOJ 4810: [Ynoi2017]由乃的玉米田 莫队,bitset

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

Description
由乃在自己的农田边散步,她突然发现田里的一排玉米非常的不美。这排玉米一共有N株,它们的高度参差不齐。
由乃认为玉米田不美,所以她决定出个数据结构题

这个题是这样的:
给你一个序列a,长度为n,有m次操作,每次询问一个区间是否可以选出两个数它们的差为x,或者询问一个区间是
否可以选出两个数它们的和为x,或者询问一个区间是否可以选出两个数它们的乘积为x ,这三个操作分别为操作1
,2,3选出的这两个数可以是同一个位置的数

Input
第一行两个数n,m
后面一行n个数表示ai
后面m行每行四个数opt l r x
opt表示这个是第几种操作,l,r表示操作的区间,x表示这次操作的x
定义c为每次的x和ai中的最大值,ai >= 0,每次的x>=2n,m,c <= 100000

Output
对于每个询问,如果可以,输出yuno,否则输出yumi

Sample 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
Sample Output
yuno

yumi

yuno

yuno

yumi

解法:qzh打野教我的,考虑一下要对区间信息查询,我们询问离线,跑个莫队,然后用bitset记录下哪些数出现了。然后区间a - b = x -> a = b + x , 同理 a + b = x -> a = x - b -> a = - (b - x),所以对于差为x我们直接把第一个bitset右移然后和自己取交,同理对于和为x的情况,我们对之前的那个bitset取反,进行类似的操作就可以了,然后对于乘法的话 a*b = x我们直接枚举质因子暴力判断即可。

复杂度 : O(n*n/32)

//BZOJ 4810

#include <bits/stdc++.h>
using namespace std;
//a - b = x -> a = b + x
//a + b = x -> a = -b + x -> a = - (b - x)
//a * b = x -> a = x / b
//O(n*n/32)

const int maxn = 100010;
const int offset = 100000;
bitset <maxn> pos;
bitset <maxn> neg;
int n, m, belong[maxn], block, a[maxn], ans[maxn], cnt[maxn];

struct node{
    int l, r, x, id, op;
    node(){}
    node(int l, int r, int x, int id, int op):l(l),r(r),x(x),id(id),op(op){}
    bool operator < (const node &rhs) const{
        if(belong[l] != belong[rhs.l]){
            return belong[l] < belong[rhs.l];
        }
        else{
            return r < rhs.r;
        }
    }
}q[maxn];

void input()
{
    scanf("%d%d", &n, &m);
    block = ceil(sqrt(n));
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
    }
    for(int i = 1; i <= n; i++){
        belong[i] = (i-1)/block+1;
    }
    for(int i = 1; i <= m; i++){
        scanf("%d%d%d%d", &q[i].op, &q[i].l, &q[i].r, &q[i].x);
        q[i].id = i;
    }
    sort(q + 1, q + m + 1);
}

void work()
{
    int L = 1, R = 0;
    for(int i = 1; i <= m; i++)
    {
        int id = q[i].id;
        while(R < q[i].r){
            R++;
            cnt[a[R]]++;
            pos[a[R]] = 1;
            neg[offset - a[R]] = 1;
        }
        while(L > q[i].l){
            L--;
            cnt[a[L]]++;
            pos[a[L]] = 1;
            neg[offset - a[L]] = 1;
        }
        while(R > q[i].r){
            cnt[a[R]]--;
            if(cnt[a[R]] == 0) pos[a[R]] = 0;
            if(cnt[a[R]] == 0) neg[offset - a[R]] = 0;
            R--;
        }
        while(L < q[i].l){
            cnt[a[L]]--;
            if(cnt[a[L]] == 0) pos[a[L]] = 0;
            if(cnt[a[L]] == 0) neg[offset - a[L]] = 0;
            L++;
        }
        if(q[i].op == 1) //+
        {
            if(((pos>>q[i].x)&pos).any()){
                ans[id] = 1;
            }
            else{
                ans[id] = 0;
            }
        }
        else if(q[i].op == 2){//-
            if(((neg>>(offset-q[i].x))&pos).any()){
                ans[id] = 1;
            }
            else{
                ans[id] = 0;
            }
        }
        else{
            if(q[i].x == 0 && pos[0])
            {
                ans[id] = 1;
                continue;
            }
            for(int j = 1; j*j <= q[i].x; j++)
            {
                if(q[i].x % j == 0 && pos[j] == 1&& pos[q[i].x/j] == 1){
                    ans[id] = 1;
                    break;
                }
            }
        }
    }
}

void output()
{
    for(int i = 1; i <= m; i++){
        puts(ans[i]?"yuno":"yumi");
    }
}

int main()
{
    input();
    work();
    output();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值