1. 程式人生 > 其它 >【洛谷】P5355 [Ynoi2017] 由乃的玉米田(莫隊)

【洛谷】P5355 [Ynoi2017] 由乃的玉米田(莫隊)

原題連結

題意

給定一個長度為 \(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\)

是否在區間中。注意到序列中的元素在 \([0,10^5]\) 內。可以發現如果 \(ix > 10^5\) 就沒有必要繼續枚舉了,也就是說,隨著 \(x\) 的增大,列舉 \(i\) 的次數會越來越少,這就啟示我們可以採用根號分治的辦法解決這個問題。

首先,對於 \(x \geq \sqrt{N}\) 的部分(\(N\) 表示序列元素的最大值,下同),直接暴力列舉 \(i\)。單次列舉次數不會超過 \(\sqrt N\),那麼總的時間複雜度也就保持在 \(O(n\sqrt N)\),可以接受。

其次,對於 \(x < \sqrt N\) 的部分。可以將 \(x\) 相同的詢問一起處理,即對於序列中的每一個 \(a[i]\)

,預處理出在 \([1,i]\) 中所有滿足商為 \(x\) 的一對數中較小座標的最大值(可以簡單理解為最近的一對數,因為要滿足兩個數都在區間中,所以取較小值),記為 \(late[i]\)。那麼對於一個詢問區間 \([l,r]\),只要滿足 \(late[r] \geq l\),那麼就必定有解,反之則無解。由於只會遍歷 \([1,\sqrt N]\) 次,每次遍歷的複雜度都為 \(O(n)\),總的時間複雜度還是維持在 \(O(n \sqrt N)\) 級別。

最後,需要注意,在預處理時, \(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;
}