回滚莫队
粘一个板子题(2025/7/30模拟赛T2)

以这道题为例
其实看到输入我就已经感觉这是莫队了,不过经过一番推理发现普通莫队做法复杂度爆炸
考虑如果只是在区间里增加元素,我们当然可以很容易就把这道题做掉,一个bitset就能解决
但在区间移动过程中还有要删去的元素,这个操作只能去暴力处理,复杂度爆炸
这时候就要用到回滚莫队了
对于这道题,添加元素很容易,删去元素很困难
那么就可以考虑

以及

对于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;
}

浙公网安备 33010602011771号