BZOJ 2794 [Poi2012]Cloakroom(离线+背包)
2794: [Poi2012]Cloakroom
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 406 Solved: 241
[Submit][Status][Discuss]
Description
有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。
Input
第一行一个正整数n (n<=1,000),接下来n行每行三个正整数,分别表示c[i], a[i], b[i] (c[i]<=1,000, 1<=a[i]<b[i]<=10^9)。
下面一行一个正整数q (q<=1,000,000),接下来q行每行三个非负整数m, k, s (1<=m<=10^9, 1<=k<=100,000, 0<=s<=10^9)。
Output
输出q行,每行为TAK (yes)或NIE (no),第i行对应第i此询问的答案。
Sample Input
5
6 2 7
5 4 9
1 2 4
2 5 8
1 3 9
5
2 7 1
2 7 2
3 2 0
5 7 2
4 1 5
6 2 7
5 4 9
1 2 4
2 5 8
1 3 9
5
2 7 1
2 7 2
3 2 0
5 7 2
4 1 5
Sample Output
TAK
NIE
TAK
TAK
NIE
NIE
TAK
TAK
NIE
HINT
题解
这题还行。
看了一会,自信满满,然后就死了。
离线把询问按s排序。把每一个物品按a[i]排序。
然后dp[i]代表当c的和能凑成i时所用的物品b的最小值的最大值。
因为排序后每一个物品只需计算一次,所以复杂度没有飞。(但至少我看来有问题)
所以对于每一个询问,只需判断dp[k]是否大于m+s就行了。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 struct thing{ 8 int c,a,b; 9 }g[2000]; 10 struct query{ 11 int l,r,k,id; 12 }q[1000100]; 13 int n,t,dp[100100],ans[1000100],mx; 14 bool cmp(thing a,thing b){ 15 return a.a<b.a; 16 } 17 bool mmp(query a,query b){ 18 return a.l<b.l; 19 } 20 int main(){ 21 scanf("%d",&n); 22 for(int i=1;i<=n;i++){ 23 scanf("%d%d%d",&g[i].c,&g[i].a,&g[i].b); 24 } 25 scanf("%d",&t); 26 for(int i=1;i<=t;i++){ 27 int s,k,m; 28 scanf("%d%d%d",&s,&k,&m); 29 q[i].l=s; 30 q[i].r=s+m; 31 q[i].k=k; 32 q[i].id=i; 33 mx=max(mx,k); 34 } 35 sort(g+1,g+1+n,cmp); 36 sort(q+1,q+1+t,mmp); 37 dp[0]=1999999999; 38 for(int i=1,now=1;i<=t;i++){ 39 while(now<=n&&g[now].a<=q[i].l){ 40 for(int j=mx;j>=g[now].c;j--){ 41 dp[j]=max(dp[j],min(dp[j-g[now].c],g[now].b)); 42 } 43 now++; 44 } 45 ans[q[i].id]=(q[i].r<dp[q[i].k]); 46 } 47 for(int i=1;i<=t;i++){ 48 if(ans[i])printf("TAK\n"); 49 else printf("NIE\n"); 50 } 51 return 0; 52 }