【bzoj4810】由乃的玉米田

Time Limit: 30 Sec Memory Limit: 256 MB

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

Solution

本题每次查询一段区间内是否出现某一特性的数,并且没有强制在线和修改(其实有修改也可以),首先肯定会考虑使用莫队算法解决此题。那么用莫队维护什么?如何得到答案呢?
对于第一个询问:查询是否存在 aiaj=x a i − a j = x ,如果暴力查询是 O(n2) O ( n 2 ) 的,无法体现莫队的优势。稍微移一下项,转化为对区间内存在的 ai a i 是否存在 aix a i − x ,复杂度为 O(n) O ( n ) ,还不够优秀,怎么办?这里用到了一个特殊的优化技巧—— bitset b i t s e t 压位大法。在跑莫队的过程中维护一个 bitset b i t s e t (设为 f f ),fi=1表示数字 i i 在区间存在,反之则不存在。那么对单个ai只需查询 [faix [ f a i − x & fai==1] f a i == 1 ] ,对整个区间只需查询 f f &(f>>x)是否存在 1 1 即可,复杂度为O(n32)
对于第二个询问:查询 ai+aj=x a i + a j = x ,移项化为查询 xai x − a i ,发现 f f 已经无法解决这项查询,因此需再维护一个bitset(设为 g g ),gnai=1表示存在 ai − a i ( n n 为可能出现的最大数),对整个区间只需查询g& (g>>(nx)) ( g >> ( n − x ) ) 是否存在 1 1 即可。
对于第三个询问:暴力枚举x的因数即可。

Code

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<bitset>
#include<cmath>
using namespace std;
const int maxn=1e5+5;
int n,m,l=1,r,siz,maxx,a[maxn],bl[maxn],cnt[maxn];
bool ans[maxn];
bitset<maxn> f,g;
struct node
{
    int op,tl,tr,num,id;
}q[maxn];
inline int input()
{
    char t=getchar();int x=0;
    while(t<48||t>57) t=getchar();
    for(;t>=48&&t<=57;t=getchar()) x=x*10+t-48;
    return x;
}
bool cmp(node x,node y)
{
    return bl[x.tl]==bl[y.tl]?x.tr<y.tr:bl[x.tl]<bl[y.tl];
}
void modify(int x,int y)
{
    x=a[x];
    if(y<0&&cnt[x]==1) f[x]=0,g[maxn-x-5]=0;
    if(y>0&&cnt[x]==0) f[x]=1,g[maxn-x-5]=1;
    cnt[x]+=y;
}
int main()
{
    n=input(),m=input(),siz=sqrt(n);
    for(int i=1;i<=n;i++) a[i]=input(),bl[i]=(i-1)/siz+1;
    for(int i=1;i<=m;i++) q[i].op=input(),q[i].tl=input(),q[i].tr=input(),q[i].num=input(),q[i].id=i;
    sort(q+1,q+1+m,cmp);
    for(int i=1;i<=m;i++)
    {
        while(r<q[i].tr) modify(r+1,1),r++;
        while(l>q[i].tl) modify(l-1,1),l--;
        while(r>q[i].tr) modify(r,-1),r--;
        while(l<q[i].tl) modify(l,-1),l++;
        bitset<maxn> temp;
        if(q[i].op==1)
        {
            temp=f,temp&=(f>>q[i].num);
            ans[q[i].id]=temp.count()?1:0;
        }
        else if(q[i].op&1)
        {
            bool flag=true;
            for(int j=1;j*j<=q[i].num&&flag;j++)
                if(q[i].num%j==0)
                {
                    int k=q[i].num/j;
                    if(cnt[j]&&cnt[k]) flag=false;
                }
            ans[q[i].id]=flag?0:1;
        }
        else
        {
            temp=f,temp&=(g>>(maxn-q[i].num-5));
            ans[q[i].id]=temp.count()?1:0;
        }
    }
    for(int i=1;i<=m;i++) puts(ans[i]?"yuno":"yumi");
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值