考虑分治,对于横跨中点的我们用dp处理,dp[i][j]表示从mid到i里面和为j的最大值里的最小值,dp1[i][j]表示从mid+1到i里面和为j的最大值的最小值,然后判断一下max(dp[l][j],dp1[r][s-j])时候小于等于c即可。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=20005;
int v[maxn];
int d[maxn];
int ans[maxn*5];
struct pi{
int l,r,c,s;
int id;
friend bool operator <(pi a,pi b){
if(a.l!=b.l) return a.l<b.l;
return a.r<b.r;
}
}pp[maxn*5];
int dp[maxn][105],dp1[maxn][105];
void divide(vector<pi>g,int l,int r){
if(g.size()==0) return;
if(l==r){
for(pi u:g){
if(u.s==v[l]&&d[l]<=u.c){
ans[u.id]=1;
}
}
return;
}
int mid=(l+r)/2;
vector<pi>g1,g2;
g1.clear();
g2.clear();
for(pi u:g){
if(u.l>mid) g2.push_back(u);
else if(u.r<=mid)
g1.push_back(u);
}
divide(g1,l,mid);
divide(g2,mid+1,r);
for(int i=mid+1;i>=mid+1;i--){
for(int j=0;j<=100;j++) dp[i][j]=2000000000;
}
dp[mid+1][0]=0;
for(int i=mid;i>=l;i--){
for(int j=0;j<=100;j++) dp[i][j]=dp[i+1][j];
for(int j=0;j<=100;j++){
if(j+v[i]<=100) dp[i][j+v[i]]=min(dp[i][j+v[i]],max(dp[i+1][j],d[i]));
}
}
for(int i=mid;i<=mid;i++){
for(int j=0;j<=100;j++){
dp1[i][j]=2000000000;
}
}
dp1[mid][0]=0;
for(int i=mid+1;i<=r;i++){
for(int j=0;j<=100;j++) dp1[i][j]=dp1[i-1][j];
for(int j=0;j<=100;j++){
if(j+v[i]<=100) dp1[i][j+v[i]]=min(dp1[i][j+v[i]],max(dp1[i-1][j],d[i]));
}
}
for(pi u:g){
if(u.l<=mid&&u.r>mid){
int l1=max(l,u.l);
int r1=min(r,u.r);
for(int j=0;j<=u.s;j++){
if(max(dp[l1][j],dp1[r1][u.s-j])<=u.c){
ans[u.id]=1;
}
}
}
}
}
int main()
{
int t,n,m;
cin>>t;
while (t--) {
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&v[i]);
for(int i=1;i<=n;i++) scanf("%d",&d[i]);
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&pp[i].l,&pp[i].r,&pp[i].c,&pp[i].s);
pp[i].id=i;
}
memset(ans,0,sizeof(ans));
vector<pi>g;
g.clear();
for(int i=1;i<=m;i++) g.push_back(pp[i]);
divide(g,1,n);
for(int i=1;i<=m;i++) printf("%d",ans[i]^1);
printf("\n");
}
}