【题解】 luogu p3674小清新人渣的本愿

  [题目链接](https://www.luogu.com.cn/problem/P3674)

算法:bitset+莫队

先把询问都离线下来,用莫队判断每个询问区间。并维护两个bitset \(s_1,s_2\),一个判断 \(a_i\)是否在当前区间内。若&a_i&在bitset1中位上的值为true,那\(n-a_i\)在bitset2中那一位上也为true。

关于操作一,其实就是询问 是否有两个数\(a,b\)在区间内,满足\(a+x=b\)。那么我们只需要把\(s1\)左移x位并&上自己,看结果是否不为0就好了,因为若不为0,说明有一个数\(a+x\)\(a\)同时在区间内。

关于操作二,考虑把它转换成操作一,若存在两个数\(a_i,b_i\),使\(a_i+b_i=x\),设\(n-a_i\)\(o\),那么\(n-o+b_i=x\),则\(o-b_i=n-x\),这样就转换成了操作一,把\(s_2\)左移\(n-x\)位然后再&上\(s_1\)看结果就好了

操作三不好用bitset做,就直接暴力枚举约数暴搞就好了,说实话这个操作和这题有点格格不入。

记得弄个桶判一下每个元素个数,这个题有重复元素。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <bitset>
#define maxn 1000005

int n,m,a[maxn],bar[maxn],len,ans[maxn],ton[maxn];
std::bitset <maxn> s1,s2,k;
struct question{
	int l,r,opt,x,whi;
}op[maxn];

bool cmp(question q,question p) {
	if (bar[q.l] == bar[p.l]) return q.r < p.r;
	else return bar[q.l] < bar[p.l];
}
void mo() {
	int ll = 1,rr = 0;
	for (int i = 1;i <= m;i++) {
		while (rr < op[i].r) {
			rr++;
			if ((++ton[a[rr]]) > 0)s1.set(a[rr]),s2.set(n - a[rr]);
		}
		while (ll > op[i].l) {
			ll--;
			if ((++ton[a[ll]]) > 0)s1.set(a[ll]),s2.set(n - a[ll]);
		}
		while (rr > op[i].r) {
			if ((--ton[a[rr]]) <= 0)s1.set(a[rr],0),s2.set(n - a[rr],0);
			rr--;
		}
		while (ll < op[i].l) {
			if (!(--ton[a[ll]]))s1.set(a[ll],0),s2.set(n - a[ll],0);
			ll++;
		}
		if (op[i].opt == 1) {
			k = (s1 & (s1 << op[i].x));
			if (k.any()) ans[op[i].whi] = 1;
		}
		else if (op[i].opt == 2) {
			k = (s1 & (s2 >> (n - op[i].x)));
			if (k.any()) ans[op[i].whi] = 1;
		}
		else {
			for (int j = 1;j <= sqrt(op[i].x);j++) {
				if (op[i].x % j == 0 && s1[j] && s1[op[i].x / j]) ans[op[i].whi] = 1;
			}
		}
	}
}
int main() {
	scanf("%d%d",&n,&m);
	for (int i = 1;i <= n;i++) {
		scanf("%d",&a[i]);
	}
	for (int i = 1;i <= m;i++) 
		scanf("%d%d%d%d",&op[i].opt,&op[i].l,&op[i].r,&op[i].x),op[i].whi = i;
	len = sqrt(n);
	for (int i = 1;i <= n;i++) {
		bar[i] = i / len + 1;
	}
	std::sort(op + 1,op + m + 1,cmp);
	mo();
	for (int i = 1;i <= m;i++) {
		if (ans[i]) printf("hana\n");
		else printf("bi\n");
	}
	return 0;
}


posted @ 2020-11-23 12:13  I11usi0ns  阅读(136)  评论(0编辑  收藏  举报