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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43824564/article/details/106870250