题意:
就是给你一个数组,然后有3种查询,分别是问你l到r区间内,是否有两个数的差等于x,只和等于x,乘积为x。
思考:
其实看到,感觉就挺复杂的,没办法去找这种关系对,除了乘积为x可以直接枚举x的因子,然后看看两个数是否都在当前的区间内。然后对于差于和的,可以维护一个bitset。bitset可以看作一个从左到右递增的数组,每个地方就是某个值,然后如果当前的数i有值的话就是1,否则就是0。如果对bitset<<,也就是扩大的意思,那么里面的元素都会往右走,因为变大了,并且里面的元素是递增排序的。如果>>就是里面的元素往左移。那么对于a-b=x,可以化简一下b = (a-x)也就是对于每个数i找一下i+x是否出现过就行了,所以直接对bit扩大x即可。但是对于加a+b=x,化简一下就是 a + (x-a)==x,也就是找到x-a这个元素。对于每个数a找到x-a,但是这怎么找呢?扩大扩小都不行,但是我们可以维护另一个bitset代表n-i是否出现过,那么我们找x-i也就是直接让n-i变小(n-x)这些位置。
发现,直接让dp2变小(n-9)=1位就行了。
代码:
struct Node{
int l,r,x;
int op,id;
}node[N];
int T,n,m,k;
int va[N];
int pos[N],cnt[N],siz;
int anw[N];
bitset<N> dp1,dp2;
bool cmp(Node A,Node B)
{
if(pos[A.l]!=pos[B.l]) return pos[A.l]<pos[B.l];
return A.r<B.r;
}
void add(int x)
{
cnt[va[x]]++;
if(cnt[va[x]]>=1)
{
dp1[va[x]] = 1;
dp2[k-va[x]] = 1;
}
}
void sub(int x)
{
cnt[va[x]]--;
if(cnt[va[x]]<=0)
{
dp1[va[x]] = 0;
dp2[k-va[x]] = 0;
}
}
bool check(int x)
{
for(int i=1;i<=x/i;i++)
{
if(x%i) continue;
if(cnt[i]>=1&&cnt[x/i]>=1) return true;
}
return false;
}
signed main()
{
IOS;
cin>>n>>m;
k = 1e5+5;
for(int i=1;i<=n;i++) cin>>va[i];
siz = sqrt(n);
for(int i=1;i<=n;i++) pos[i] = i/siz;
for(int i=1;i<=m;i++)
{
int op,a,b,c;
cin>>op>>a>>b>>c;
node[i] = {a,b,c,op,i};
}
sort(node+1,node+1+m,cmp);
int l = 1,r = 0;
for(int i=1;i<=m;i++)
{
while(l<node[i].l) sub(l++);
while(l>node[i].l) add(--l);
while(r<node[i].r) add(++r);
while(r>node[i].r) sub(r--);
int op = node[i].op,x = node[i].x;
if(op==1)
{
if((dp1&(dp1<<x)).any())
anw[node[i].id] = 1;
}
if(op==2)
{
if((dp1&(dp2>>(k-x))).any())
anw[node[i].id] = 1;
}
if(op==3)
{
if(check(x))
anw[node[i].id] = 1;
}
}
for(int i=1;i<=m;i++)
{
if(anw[i]) cout<<"hana\n";
else cout<<"bi\n";
}
return 0;
}
总结:
多多思考,多多理解本质。