[THUPC2017]天天爱射击 题解(主席树)

传送门:cogs2897
在网上搜了一圈,只有可怜的一篇题解,是整体二分的。其实这题可以不用整体二分(我不会整体二分)。
二分的思想是,对于每块木板,可以二分它被打碎的时间,然后用数据结构来check,时间复杂度两个log。
那你既然写了数据结构为何还要二分呢(逃
对于每块模板,直接查询它被打碎的时间。如果把每个子弹的位置作为下标,时间作为值,就相当于是查询区间上的第s小值,主席树模板题。
并且主席树应该是可以回避一个位置上多个子弹的问题的,因为它是权值线段树。然而我比较菜,就用了一种很奇怪的做法(我也不知道有没有更好的写法)。对每个位置我都开了一个vector,建树的时候就…还是看代码吧我感觉我很难说清楚了

#include <cctype>
#include <cstdio>
#include <climits>
#include <algorithm>
#include <vector>
 
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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值