P3674 小清新人渣的本願 莫隊+bitset 維護區間是否存在兩個數 相減、相加、相乘爲x

bitset都沒怎麼用過 這題恰好能熟悉一下

這題一看就是莫隊吧  然後如何看區間裏面是否有兩個數相減 或相加 或相乘爲k

先講相乘 因爲這個最簡單  

我們直接暴力遍歷k的因子  如果 y和k/y 都在的話 那麼就可以  反正是帶根號的 和莫隊的複雜度一樣 沒什麼問題

然後再說相減   

 對於每個數 我們用bitset記錄它是否出現過   bitset s1記錄 a[i] 的出現

如果區間中存在 y,q  y-q=k  即 y = k+q 

那麼顯然s1得滿足  (s1&(s1<<k)) 存在任意一位爲1

最後說相加 相加比較抽象 得轉換一下  對於 bitset s2 我們記錄 maxn-a[i]的出現 maxn是大於等於值域的一個數

如果  y+q=x   

也就是 (maxn-y)-(maxn-x)=q 

那麼就是 s1&(s2>>(maxn-x)) 存在任意位爲1

其中存在任意位爲1 可以用bitset的.any()函數

另外通過樣例我們知道  每個數其實可以重複選  需要注意一下

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int M = 1e5;
int sqn,n,m,a[N],cnt[N];
bool ans[N];
bitset<N>s1,s2;
inline int in(){
	int x=0;char c=0;
	while(c>'9'||c<'0') c=getchar();
	while(c<='9'&&c>='0') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x; 
}
struct node{
	int l,r,lbk,id,op,x;
	bool operator < (const node &a){
		if(lbk==a.lbk) return r<a.r;
		else return lbk<a.lbk;
	}
}q[N];

void add(int x){
	s1[a[x]]=s2[M-a[x]]=++cnt[a[x]];
}

void del(int x){
	s1[a[x]]=s2[M-a[x]]=--cnt[a[x]]!=0;
}
int main(){
	n=in(),m=in();
	for(int i = 1; i <= n; i++) a[i]=in();
	sqn=sqrt(n);
	for(int i = 1; i <= m; i++){
		q[i].op=in(),q[i].l=in(),q[i].r=in(),q[i].x=in();
		q[i].lbk=(q[i].l-1)/sqn+1;
		q[i].id=i;
	}
	sort(q+1,q+1+m);
	int l=1,r=0;
	for(int i = 1; i <= m; i++){
		while(l<q[i].l) del(l++);
		while(l>q[i].l) add(--l);
		while(r<q[i].r)	add(++r);
		while(r>q[i].r)	del(r--);
		if(q[i].op==1){
			ans[q[i].id]=(s1&(s1<<q[i].x)).any();
		}else if(q[i].op==2){
			ans[q[i].id]=(s1&(s2>>(M-q[i].x))).any();
		}else{
			for(int j = 1; j*j <= q[i].x; j++)
			if(q[i].x%j==0)
				if(s1[j]&&s1[q[i].x/j]) ans[q[i].id]=true;
		}
	}
	for(int i = 1; i <= m; i++) if(ans[i]) printf("hana\n"); else printf("bi\n");
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章