peiwenjun's blog 没有知识的荒原

P5355 [Ynoi2017] 由乃的玉米田 题解

题目描述

给定长为 \(n\) 的序列 \(a\)\(m\) 次询问:

  • 1 l r :能否在区间 \([l,r]\) 中选两个和为 \(x\) 的数。
  • 2 l r :能否在区间 \([l,r]\) 中选两个差为 \(x\) 的数。
  • 3 l r :能否在区间 \([l,r]\) 中选两个积为 \(x\) 的数。
  • 4 l r :能否在区间 \([l,r]\) 中选两个商为 \(x\) 的数。(要求整除)

选择的数可以相同。

数据范围

  • \(1\le n,m,a_i\le 10^5,0\le x\le 10^5\)

时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{128MB}\)

分析

显然我们可以用莫队求出对每个询问,哪些数在区间 \([l,r]\) 中出现过,用 bitset 存储。

差的询问是最容易的,判断 b&(b>>x) 是否为空即可。

和的询问需要稍微转化一下,对每个在区间中出现的数 \(i\) ,令 c[v-i]=1

c 同样可以用莫队维护,询问只需要判断 b&(c>>(v-x)) 是否为空。

如果两数之积为 \(x\) ,那么至少有一个数 \(\le\sqrt x\)

积的询问枚举 \(i\le\sqrt x\) ,判断 \(i\)\(\frac xi\) 是否同时出现即可。


除的询问稍微麻烦一点,对 \(x\) 根号分治。

如果 \(x\gt B\) ,枚举 \(i\le\frac vB\) ,判断 \(i\)\(ix\) 是否同时出现即可。

如果 \(x\le B\) ,枚举 \(x\) ,将所有 \(x\) 的询问放在一起做。

预处理 \(lim_i\) 表示,当右端点为 \(i\) 时,满足区间内存在两个数商为 \(x\) 的最大左端点,那么询问只需要判断 \(l\le lim_r\) 是否成立。

时间复杂度 \(\mathcal O(n\sqrt m+\frac{n^2}w)\)

#include<bits/stdc++.h>
using namespace std;
const int B=320,v=1e5,maxn=1e5+5;
int m,n,q;
int a[maxn],bel[maxn],cnt[maxn],res[maxn];
int lim[maxn],lst[maxn];
bitset<maxn> b,c;
struct quer
{
    int op,l,r,x,id;
}f[maxn];
vector<quer> g[maxn];
inline int read()
{
    int q=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) q=10*q+ch-'0',ch=getchar();
    return q;
}
inline bool cmp(quer a,quer b)
{
    if(bel[a.l]!=bel[b.l]) return bel[a.l]<bel[b.l];
    return bel[a.l]&1?a.r<b.r:a.r>b.r;
}
inline void add(int x)
{
    if(!cnt[x]++) b[x]=c[v-x]=1;
}
inline void del(int x)
{
    if(!--cnt[x]) b[x]=c[v-x]=0;
}
int main()
{
    n=read(),q=read();
    for(int i=1;i<=n;i++) a[i]=read(),bel[i]=(i-1)/B+1;
    for(int i=1;i<=q;i++)
    {
        int op=read(),l=read(),r=read(),x=read();
        if(op==4&&x<=B) g[x].push_back({op,l,r,x,i});
        else f[++m]={op,l,r,x,i};
    }
    sort(f+1,f+m+1,cmp);
    for(int i=1,l=1,r=0;i<=m;i++)
    {
        while(l>f[i].l) add(a[--l]);
        while(r<f[i].r) add(a[++r]);
        while(l<f[i].l) del(a[l++]);
        while(r>f[i].r) del(a[r--]);
        int x=f[i].x,id=f[i].id,op=f[i].op;
        if(op==1) res[id]=(b&(b>>x)).any();
        if(op==2) res[id]=(b&(c>>(v-x))).any();
        if(op==3) for(int i=1;i*i<=x;i++) res[id]|=x%i==0&&b[i]&&b[x/i];
        if(op==4) for(int i=x;i<=v;i+=x) res[id]|=b[i]&&b[i/x];
    }
    for(int i=1;i<=B;i++)
    {
        if(g[i].empty()) continue;
        memset(lst,0,sizeof(lst));
        for(int j=1;j<=n;j++)
        {
            lim[j]=lim[j-1],lst[a[j]]=j;
            if(a[j]%i==0) lim[j]=max(lim[j],lst[a[j]/i]);
            if(a[j]*i<=v) lim[j]=max(lim[j],lst[a[j]*i]);
        }
        for(auto p:g[i]) res[p.id]=p.l<=lim[p.r];
    }
    for(int i=1;i<=q;i++) printf(res[i]?"yuno\n":"yumi\n");
    return 0;
}

posted on 2023-05-09 17:41  peiwenjun  阅读(13)  评论(0)    收藏  举报

导航