小清新人渣的本愿
这两天写了些 b i t s e t bitset bitset的题,但都不想写题解。。。正巧这道题还结合了莫队,也是正在学习的,就记录一下吧。
题意:
给定一个 a a a数组,有三种询问:
- 询问 [ l , r ] [l,r] [l,r]区间中是否有差为 x x x的数对;
- 询问 [ l , r ] [l,r] [l,r]区间中是否有和为 x x x的数对;
- 询问 [ l , r ] [l,r] [l,r]区间中是否有积为 x x x的数对。
思路:
- 先只考虑三种询问后面的部分,不考虑区间问题。
- 要求是否存在差为 x x x的数对,显然只需要知道每个数向前(或向后) x x x的数值是否存在,要检索所有数字的话,考虑使用 b i t s e t bitset bitset的 ( b (b (b& b b b<< x ) . a n y x).any x).any()就可以知道是否存在啦!
- 要求是否存在和为 x x x的数对,有一个小技巧,同时维护一个反转的 b i t s e t bitset bitset,想想将反转的 b i t s e t bitset bitset向右移动 N N N( a a a数组的值域)位,肯定这个 b i t s e t bitset bitset就全为 0 0 0了;但如果少移动 x x x个位置的话,就相当于把原状态中前 [ 0 , x ] [0,x] [0,x]的部分保留了,并且是反转的;显然将原 b i t s e t bitset bitset和这个经过处理的 b i t s e t bitset bitset取与,就可以知道是否有和为 x x x的数对啦!
- 而积为 x x x是否存在反而最简单,考虑这个小问题的复杂度, m m m ( ≤ 1 e 5 ) (≤1e5) (≤1e5)个询问, x x x又是小于 1 e 5 1e5 1e5的,因此枚举 x x x所有较小因数的复杂度为 O ( m ∗ x ) O(m*\sqrt{x}) O(m∗x),完全是可以接受的!
- 至此,此问题就只剩维护区间状态的问题了;上述三种问题都指向了一种状态——某个数字是否存在,这恰好是莫队的常见问题,不难想出。因此,总复杂度为莫队的端点移动 ∗ * ∗每个询问的答案判断,为 O ( n 3 2 ∗ ( n + n 32 ) ) O(n^\frac{3}{2}*(\sqrt{n}+\frac{n}{32})) O(n23∗(n+32n))(其中 m m m和 a a a的值域都用 n n n代替了),感觉还是能被卡住的。
代码
#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}
const int maxn = 1e5+10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-7;
const int N = 100001;
int n, m, len;
int a[maxn], cnt[maxn], ans[maxn];
bitset<N+1> b1, b2;
struct P{
int opt, l, r, x, id;
friend bool operator < (const P &a, const P &b) {
if((a.l-1)/len!=(b.l-1)/len) return a.l<b.l;
if((a.l-1)/len%2) return a.r>b.r;
return a.r<b.r;
}
}p[maxn];
inline void del(int x) {
if(--cnt[x]==0) b1[x]=0, b2[N-x]=0;
}
inline void add(int x) {
if(cnt[x]++==0) b1[x]=1, b2[N-x]=1;
}
int main() {
//ios::sync_with_stdio(false); cin.tie(0);
n=read(), m=read();
for(int i=1; i<=n; ++i) a[i]=read();
for(int i=1; i<=m; ++i) {
int opt=read(), l=read(), r=read(), x=read();
p[i]=(P){opt,l,r,x,i};
}
len=sqrt(m);
sort(p+1,p+1+m);
int l=1, r=0;
for(int i=1; i<=m; ++i) {
while(l<p[i].l) del(a[l++]);
while(l>p[i].l) add(a[--l]);
while(r<p[i].r) add(a[++r]);
while(r>p[i].r) del(a[r--]);
if(p[i].opt==1) ans[p[i].id]=(b1&b1<<p[i].x).any()?1:0;
else if(p[i].opt==2) ans[p[i].id]=(b1&b2>>(N-p[i].x)).any()?1:0;
else {
bool f=0;
for(int j=1; j*j<=p[i].x; ++j) {
if(p[i].x%j==0&&b1[j]&&b1[p[i].x/j]) { f=1; break; }
}
ans[p[i].id]=f;
}
}
for(int i=1; i<=m; ++i) {
if(ans[i]) printf("hana\n");
else printf("bi\n");
}
}