题目:
https://www.luogu.org/problemnew/show/P3674
题目大意:
给你一个长度为
N
N
的序列,每次询问[l,r]能不能取出两个数,和或差或积等于,记为询问1,2,3。其中
N<=1e5
N
<=
1
e
5
,序列中每一个数包括询问中的数
k<=1e5
k
<=
1
e
5
分析:显然可以用bitset做到 c/64 c / 64 的复杂度求两个数是否差为C,加法维护一个反的bitset,乘法直接暴力。可以卡过去,时间复杂度有点玄学。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <bitset>
#include <algorithm>
const int maxn=1e5+7;
using namespace std;
bitset <maxn> A,B;
int a[maxn],b[maxn];
int belong[maxn];
int n,m,l,r;
struct node{
int l,r,num,op,x,ans;
}q[maxn];
bool cmp1(node x,node y)
{
if (belong[x.l]==belong[y.l]) return x.r<y.r;
return x.l<y.l;
}
bool cmp2(node x,node y)
{
return x.num<y.num;
}
void updata(int x,int c)
{
if (c==1)
{
b[a[x]]++;
if (b[a[x]]>=1)
{
A[a[x]]=1;
B[maxn-a[x]]=1;
}
}
else
{
b[a[x]]--;
if (b[a[x]]<1)
{
A[a[x]]=0;
B[maxn-a[x]]=0;
}
}
}
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",&q[i].op,&q[i].l,&q[i].r,&q[i].x);
q[i].num=i;
}
int block=trunc(sqrt(n));
for (int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
sort(q+1,q+m+1,cmp1);
l=1; r=1;
updata(1,1);
for (int i=1;i<=m;i++)
{
for (r;r<q[i].r;r++) updata(r+1,1);
for (r;r>q[i].r;r--) updata(r,-1);
for (l;l<q[i].l;l++) updata(l,-1);
for (l;l>q[i].l;l--) updata(l-1,1);
if (q[i].op==1)
{
q[i].ans=((A&(A>>q[i].x)).count());
}
if (q[i].op==2)
{
q[i].ans=((A&(B>>(maxn-q[i].x))).count());
}
if (q[i].op==3)
{
q[i].ans=0;
for (int j=1;j*j<=q[i].x;j++)
{
if (q[i].x%j!=0) continue;
if ((b[j]) && (b[q[i].x/j]))
{
q[i].ans=1;
break;
}
}
}
}
sort(q+1,q+m+1,cmp2);
for (int i=1;i<=m;i++)
{
if (q[i].ans) printf("hana\n");
else printf("bi\n");
}
}