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;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17385815.html
浙公网安备 33010602011771号