【洛谷】P5355 [Ynoi2017] 由乃的玉米田(莫隊)
阿新 • • 發佈:2022-01-25
題意
給定一個長度為 \(n\) 的序列,有 \(m\) 次詢問,每次詢問給出一個區間 \([l,r]\) 和一個 \(x\),問區間內是否存在兩個數(可以相同),使得它們的和或差或積或商等於 \(x\) 。
資料範圍
所有輸入的數在 \([0,10^5]\) 內,序列中的元素在 \([0,10^5]\) 內。
思路
對於詢問和、差、積的操作,直接按照 P3674 的做法求解即可。這裡主要討論一下如何解決詢問商的操作。
若想在給定區間中找到一對 \(a,b\),使得 \(a \div b = x\) 。最樸素的想法就是從 \(1\) 到 \(10^5\) 列舉 \(i\),判斷 \(i\) 以及 \(ix\)
首先,對於 \(x \geq \sqrt{N}\) 的部分(\(N\) 表示序列元素的最大值,下同),直接暴力列舉 \(i\)。單次列舉次數不會超過 \(\sqrt N\),那麼總的時間複雜度也就保持在 \(O(n\sqrt N)\),可以接受。
其次,對於 \(x < \sqrt N\) 的部分。可以將 \(x\) 相同的詢問一起處理,即對於序列中的每一個 \(a[i]\)
最後,需要注意,在預處理時, \(a[j]\) 即可以是除數,也可以是被除數,不要忘了判斷 \(a[j]/x\) (當且僅當 \(a[j] \mod x=0\)
code:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<bitset>
using namespace std;
const int N=1e5+10;
const int K=320;
bitset<N> s1,s2;
int tot,len,n,m,a[N],cnt[N],las[N],lat[N]; int ans[N];
struct query{int opt,l,r,x,id;}q[N];
vector<query>q1[N];
bool cmp(query a,query b){int i=a.l/len,j=b.l/len; return i!=j?i<j:a.r<b.r;}
void add(int x){cnt[x]++;if(cnt[x]==1) s1[x]=1,s2[N-x]=1;}
void del(int x){cnt[x]--;if(cnt[x]==0) s1[x]=0,s2[N-x]=0;}
int main()
{
scanf("%d%d",&n,&m); len=n/sqrt(m);for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int opt,l,r,x,i=1;i<=m;i++)
{
scanf("%d%d%d%d",&opt,&l,&r,&x);
if(opt!=4||x>K) q[++tot]=query{opt,l,r,x,i};
else q1[x].push_back(query{opt,l,r,x,i});
}
for(int i=1;i<=K;i++)
{
if(!q1[i].size()) continue;
memset(las,0,sizeof(las));memset(lat,0,sizeof(lat));int now=0;
for(int j=1;j<=n;j++)
{
las[a[j]]=j; //別忘了更新每個數最後出現的位置,因為可以是同一個數,所以放在判斷前更新
if(i*a[j]<N) now=max(now,las[a[j]*i]);
if(a[j]%i==0) now=max(now,las[a[j]/i]);
lat[j]=now;
}
for(int j=0;j<q1[i].size();j++)
ans[q1[i][j].id]=lat[q1[i][j].r]>=q1[i][j].l;
}
sort(q+1,q+tot+1,cmp);
for(int i=0,j=1,k=1;k<=tot;k++)
{
int l=q[k].l,r=q[k].r,id=q[k].id,x=q[k].x;
while(i<r) add(a[++i]);
while(i>r) del(a[i--]);
while(j>l) add(a[--j]);
while(j<l) del(a[j++]);
if(q[k].opt==1) ans[id]=(s1&(s1<<x)).any();
else if(q[k].opt==2) ans[id]=(s1&(s2>>(N-x))).any();
else if(q[k].opt==3)
{
for(int t=1;t*t<=x;t++)
if(x%t==0&&(s1[t]&s1[x/t])){ans[id]=true;break;}
}
else
{
for(int t=1;t*x<N;t++)
if(s1[t]&&s1[t*x]) {ans[id]=true;break;}
}
}
for(int i=1;i<=m;i++) puts(!ans[i]?"yumi":"yuno");
return 0;
}