算法: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
");
else printf("bi
");
}
return 0;
}