洛谷-小清新人渣的本愿-(莫队+bitset)

小清新人渣的本愿

题意:
就是给你一个数组,然后有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;
}

总结:
多多思考,多多理解本质。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值