传送门:cogs2897
在网上搜了一圈,只有可怜的一篇题解,是整体二分的。其实这题可以不用整体二分(我不会整体二分)。
二分的思想是,对于每块木板,可以二分它被打碎的时间,然后用数据结构来check,时间复杂度两个log。
那你既然写了数据结构为何还要二分呢(逃
对于每块模板,直接查询它被打碎的时间。如果把每个子弹的位置作为下标,时间作为值,就相当于是查询区间上的第s小值,主席树模板题。
并且主席树应该是可以回避一个位置上多个子弹的问题的,因为它是权值线段树。然而我比较菜,就用了一种很奇怪的做法(我也不知道有没有更好的写法)。对每个位置我都开了一个vector,建树的时候就…还是看代码吧我感觉我很难说清楚了
#include
#include
#include
#include
#include
template <typename T> inline void read(T& t) {
int f = 0, c = getchar(); t = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) t = t * 10 + c - 48, c = getchar();
if (f) t = -t;
}
#if __cplusplus >= 201103L
template <typename T, typename... Args>
inline void read(T& t, Args&... args) {
read(t); read(args...);
}
#else
template <typename T1, typename T2>
inline void read(T1& t1, T2& t2) { read(t1); read(t2); }
template <typename T1, typename T2, typename T3>
inline void read(T1& t1, T2& t2, T3& t3) { read(t1, t2); read(t3); }
template <typename T1, typename T2, typename T3, typename T4>
inline void read(T1& t1, T2& t2, T3& t3, T4& t4) { read(t1, t2, t3); read(t4); }
template <typename T1, typename T2, typename T3, typename T4, typename T5>
inline void read(T1& t1, T2& t2, T3& t3, T4& t4, T5& t5) { read(t1, t2, t3, t4); read(t5); }
#endif // C++11
#ifdef WIN32
#define LLIO "%I64d"
#else
#define LLIO "%lld"
#endif // WIN32 long long
#define rep(I, A, B) for (int I = (A); I <= (B); ++I)
#define rrep(I, A, B) for (int I = (A); I >= (B); --I)
#define erep(I, X) for (int I = head[X]; I; I = next[I])
const int maxn = 2e5 + 207;
int lx[maxn], rx[maxn], sx[maxn];
int left[maxn << 5], right[maxn << 5], sum[maxn << 5], root[maxn];
// 本来应该是 int a[maxn],但是要考虑一个位置上有多个值的情况(有点像哈希表冲突)
// 所以我就用了类似拉链法的做法,开了个vector(感觉很蠢)
std::vector<int> a[maxn];
int ans[maxn];
// all用来记录位置最远到哪里
int n, m, tot, all;
void insert(int &curr, int l, int r, int pos) {
sum[++tot] = sum[curr] + 1;
left[tot] = left[curr];
right[tot] = right[curr];
curr = tot;
if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid)
insert(left[curr], l, mid, pos);
else
insert(right[curr], mid + 1, r, pos);
}
int query(int lt, int rt, int l, int r, int k) {
if (l == r) return l;
int s = sum[left[rt]] - sum[left[lt]];
int mid = (l + r) >> 1;
if (k <= s)
return query(left[lt], left[rt], l, mid, k);
else
return query(right[lt], right[rt], mid + 1, r, k - s);
}
int main() {
freopen("shooting.in", "r", stdin);
freopen("shooting.out", "w", stdout);
read(n, m);
rep(i, 1, n) {
read(lx[i], rx[i], sx[i]);
all = std::max(all, rx[i]);
}
rep(i, 1, m) {
int x; read(x);
a[x].push_back(i);
all = std::max(all, x);
}
rep(i, 1, all) {
root[i] = root[i - 1];
// 对每个值都依次插入主席树
if (!a[i].empty()) {
for (unsigned j = 0; j < a[i].size(); ++j)
insert(root[i], 1, m, a[i][j]);
}
}
rep(i, 1, n) {
// 可能出现木板根本没被打碎的情况
if (sum[root[rx[i]]] - sum[root[lx[i] - 1]] < sx[i]) continue;
int rp = query(root[lx[i] - 1], root[rx[i]], 1, m, sx[i]);
++ans[rp];
}
rep(i, 1, m) printf("%d\n", ans[i]);
return 0;
}