Task
有n件物品,每件物品有三個屬性a[i], b[i], c[i] .(a[i]<b[i])
再給出q個詢問,每個詢問由非負整數m, k, s組成,問是否能夠選出某些物品使得:
1. 對於每個選的物品i,滿足a[i]<=m且b[i]>m+s。
2. 所有選出物品的c[i]的和正好是k。
n<=1,000, q<=1,000,000.
c[i]<=1,000, 1<=a[i]<b[i]<=10^9
1<=m<=10^9, 1<=k<=100,000, 0<=s<=10^9
Solution
離線.
把詢問和節點都根據左端點排序,保證詢問[l,r]時,所有考慮的物品的左端點都滿足條件.滿足左端點的物品個數是不斷增加的,一旦滿足,接下來所有詢問都會滿足.那麼現在只用考慮右端點了.
直接確定所有物品再進行01揹包肯定不行.那麼考慮新思路.假如因爲現在可選物品的範圍一直增大,我們可以對當前物品的信息進行記錄.由於k並不大,可以考慮把k存入下標:
設dp[x]爲當前價值構成x最遠的b[i].
對於詢問可以直接判斷dp[k]與m+s的關係,
對於更新,用01揹包進行更新即可.
題外話:
這道題的思路來源於BestCoder round#86 1005(hdu5808,題解戳這,題目鏈接戳這)的思路,都是詢問一些符合範圍的物品價值是否能夠達到k.而且思路也是相近的,只不過BC的這道題信息多一維,用了分治來優化,思路更復雜一些.
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
#include<queue>
#include<set>
using namespace std;
inline void rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),c>=48);
}
inline void print(ll x){
if(!x)return ;
print(x/10);
putchar((x%10)^48);
}
inline void sc(ll x){
if(x<0){x=-x;putchar('-');}
print(x);
if(!x)putchar('0');
putchar('\n');
}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
const int M=1005;
const int N=1e6+5;
const int P=100000;
int dp[P+5],n,m,ans[N];
char s[2][3];
struct node{
int l,r,v,id;
node(){id=-1;}
bool operator<(const node &tmp)const{
if(l!=tmp.l)return l<tmp.l;
return id<tmp.id;
}
}A[M+N];
void pt(int c){
for(int i=0;i<3;i++)putchar(s[c][i]);
putchar('\n');
}
int main(){
int n,i,j,mx=0,sum=0,cas,a,b,k;
memset(dp,-1,sizeof(dp));
rd(n);
s[0][0]='N',s[0][1]='I',s[0][2]='E';
s[1][0]='T',s[1][1]='A',s[1][2]='K';
for(i=1;i<=n;i++){
rd(A[i].v);rd(A[i].l);rd(A[i].r);
Max(mx,A[i].r);
}
rd(m);
for(i=1;i<=m;i++){
rd(A[i+n].l);rd(A[i+n].v);rd(A[i+n].r);
A[i+n].r+=A[i+n].l;
A[i+n].id=i;
}
sort(A+1,A+1+n+m);
dp[0]=mx+1;
for(i=1;i<=n+m;i++){
int l=A[i].l,r=A[i].r,v=A[i].v,id=A[i].id;
if(~id){//詢問
if(~dp[v]&&dp[v]>r)ans[id]=1;
else ans[id]=0;
continue;
}//update (j+v)<=P j<=P-v
for(j=min(P-v,sum);j>=0;j--){
if(~dp[j])Max(dp[v+j],min(dp[j],r));//更新dp值
}
sum+=v;
}
for(i=1;i<=m;i++)pt(ans[i]);
return 0;
}