回滚莫队

粘一个板子题(2025/7/30模拟赛T2)
image
以这道题为例
其实看到输入我就已经感觉这是莫队了,不过经过一番推理发现普通莫队做法复杂度爆炸
考虑如果只是在区间里增加元素,我们当然可以很容易就把这道题做掉,一个bitset就能解决
但在区间移动过程中还有要删去的元素,这个操作只能去暴力处理,复杂度爆炸
这时候就要用到回滚莫队了
对于这道题,添加元素很容易,删去元素很困难
那么就可以考虑
image
以及
image
对于l r在一个块里的,直接暴力扫一遍即可,毕竟每个块大小只有\(\sqrt m\)
那么代码就是这样了,具体的细节以后理解加深后慢慢补充吧

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=510;
int read(){
	int x;scanf("%d",&x);return x;
}
int a[N],bl[N],ans[N],R[N];
bitset<M>f,ff;
struct node{
	int l,r,k,id;
}b[N];
bool cmp(node a,node b){
	return (bl[a.l]==bl[b.l])?a.r<b.r:bl[a.l]<bl[b.l];
}
int ask(int l,int r,int k){
	bitset<M>F;
	F[0]=1;
	for(int i=l;i<=r;i++)
		F|=(F<<a[i]);
	for(int j=k;j>=0;j--)
		if(F[j]) return j;
	return 0;
}
//int t[N];
int main(){
//	freopen("bag5.in","r",stdin);
	freopen("bag.in","r",stdin);
	freopen("bag.out","w",stdout);
	int n=read(),m=read();
	int bel=max(1,int(n/sqrt(m)));
	for(int i=1;i<=n;i++){
		a[i]=read();
		bl[i]=(i-1)/bel+1;
	}
	for(int i=1;i<=bl[n];i++)
		if(i==bl[n]) R[i]=n;
		else R[i]=bel*i;
	for(int i=1;i<=m;i++)
		b[i]={read(),read(),read(),i};
	sort(b+1,b+m+1,cmp);
	int block=0,l=0,r=0,p;
	for(int i=1;i<=m;i++){
		if(bl[b[i].l]==bl[b[i].r]){
			ans[b[i].id]=ask(b[i].l,b[i].r,b[i].k);
//			t[b[i].id]=b[i].k;
			continue;
		}
		int now=bl[b[i].l];
		if(block!=now){
			f.reset();
			l=R[now],r=l-1;
			f[0]=1;
			block=now;
		}
		while(r<b[i].r) f|=(f<<a[++r]);
		p=l,ff=f;
		while(p>b[i].l) ff|=(ff<<a[--p]);
		for(int j=b[i].k;j>=0;j--)
			if(ff[j]){
				ans[b[i].id]=j;
//				t[b[i].id]=b[i].k;
				break;
			}
	}
//	int cnt=0;
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
//		if(t[i]!=ans[i])
//			printf("%d %d\n",t[i],ans[i]);
//		if(t[i]!=ans[i]) cnt++;
//	cout<<m<<' '<<cnt;
	return 0;
}
posted @ 2025-07-30 17:03  追旭  阅读(30)  评论(0)    收藏  举报
//雪花飘落效果