题面
链接在这里(洛谷)
bzoj上是权限题哇qwq
简述题意:
有
件物品,每件物品有三个属性
。
再给出
个询问,每个询问由非负整数
组成,问是否能够选出某些物品使得:
。
分析
最开始考虑的是限制条件对应一个二维线段树的区间,但是想了想好想不太能搞,合并的复杂度有点上天。
那么首先考虑退化退化版本,没有
的限制,那么明显是一个可达性dp
继续考虑退化版本,如果只有
的限制怎么处理。
注意到一般的可达性dp中从未利用过物品的访问顺序,于是我们考虑将物品按照
排序,当访问到一个
的时候就把恰好大于他的询问都处理。
现在需要考虑怎么加入另一个限制。其实我们只需求一个不用离线排序的做法做刚刚那个退化问题,然后结合刚刚算法就可以了。
注意到我们另一个用的不太充分的东西是
,一般的可达性dp中
,利用效率不高。于是令
表示拼出
的数值的所有方案中,最小的
最大能是几,于是我们只需在询问的时候去看
是否大于
即可。
于是我们解决了这个问题。
思路总结
在对一个问题进行思考的时候,可以首先考虑思考他的退化版本,然后考虑我们有哪些条件/变量/性质/..应用不够充分,再思考能否加以利用解决更强的问题。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <bits/stdc++.h>
#include <set>
#include <queue>
#define MAXN
#define ri register int
using namespace std;
/*有n件物品,每件物品有三个属性a[i], b[i], c[i] (a[i]<b[i])。
再给出q个询问,每个询问由非负整数m, k, s组成,问是否能够选出某些物品使得:
对于每个选的物品i,满足a[i]<=m且b[i]>m+s。
所有选出物品的c[i]的和正好是k。*/
int n, Q, dp[100050], ans[1000050];
struct node{
int a, b, c, id;
bool operator < (const node &x) const {
return a < x.a;
}
}obj[1050], q[1000050];
int main() {
scanf("%d", &n);
for(ri i = 1; i <= n; i++) scanf("%d%d%d", &obj[i].c, &obj[i].a, &obj[i].b);
scanf("%d", &Q);
for(ri i = 1; i <= Q; i++) scanf("%d%d%d", &q[i].a, &q[i].c, &q[i].b), q[i].id = i;
sort(obj+1, obj+n+1); sort(q+1, q+Q+1);
int pos = 1;
dp[0] = 2e9;
for(ri i = 1; i <= n; i++) {
//cout<<obj[i].a<<' '<<obj[i].b<<' '<<obj[i].c<<'\n';
while(q[pos].a < obj[i].a && pos <= Q) {
//cout<<pos<<' '<<q[pos].a<<' '<<q[pos].b<<' '<<q[pos].c<<' '<<dp[q[pos].c]<<'\n';
ans[q[pos].id] = (dp[q[pos].c] > q[pos].a+q[pos].b);
pos++;
}
for(ri j = 100000; ~j; j--)
dp[j+obj[i].c] = max(dp[j+obj[i].c], min(dp[j], obj[i].b));
//for(ri j = 0; j <= 5; j++) cout<<dp[j]<<' ';
//cout<<'\n';
}
while(pos <= Q) {
//cout<<pos<<' '<<q[pos].a<<' '<<q[pos].b<<' '<<q[pos].c<<' '<<dp[q[pos].c]<<'\n';
ans[q[pos].id] = (dp[q[pos].c] > q[pos].a+q[pos].b);
pos++;
}
for(ri i = 1; i <= Q; i++) printf(ans[i]?"TAK\n":"NIE\n");
return 0;
}