题解 P7424 [THUPC2017] 天天爱射击

原题

本题有很多种做法,这里提供了一种应该是理论时间复杂度最优的 O(nlogn) 的主席树做法,但因为常数巨大被整体二分吊打……

题目描述挺好理解的,问题在于怎么转换题意。

直接顺着题意在线做复杂度肯定是不行的,简单思考后发现把题意转化成求每个木板被哪颗子弹击中即可。

对于第 i 块木板所在的位置 liri,可视为区间 [li,ri],而击碎它的子弹就是第 si 颗击中它的子弹。

于是想到一个典型的问题:静态区间第 k 小。

这里的关键字是每颗子弹发射的先后顺序,对于 x 轴上的第 j 个点记一个 tj,表示在点 j 上的子弹发射的顺序编号,若点 j 上没有发射过子弹,tjinf。对于区间 [li,ri],查询此区间内第 Si 大的 tj 并返回点 j 上查询到的子弹。

于是可以用主席树处理这样的问题,时间复杂度即为主席树的 O(nlogn),空间 O(nlogn)

有个重要的内容,每个点上可能有多个子弹,所以每个点开一个 vector,然后把该点所存的所有子弹顺序编号都放进主席树的这棵分树中,不影响区间查询的正确性。因此主席树的空间也要开大一倍,来处理所有的子弹都在一个点的那种极限数据。

这道题还是挺好想的,思维量不大也很好写。

具体的某些实现可以看看代码。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, LOG = 20, inf = 1e9;
int n, m, t;
int l[N], r[N], s[N], ans[N];
int rt[N], val[N << 1];
int cnt;
vector <int> a[N]; 
struct President_Tree {
    int sum, l, r;
} tree[(N << 1) * LOG];
void build(int &node, int l, int r) {
    node = ++cnt;
    tree[node].sum = 0;
    if(l == r) 
        return;
    int mid = l + r >> 1;
    build(tree[node].l, l, mid);
    build(tree[node].r, mid + 1, r);
}
void update(int &node, int pre, int l, int r, int x) {
    node = ++cnt;
    tree[node] = tree[pre];
    tree[node].sum = tree[pre].sum + 1;
    if(l == r) 
        return;
    int mid = l + r >> 1;
    if(x <= mid)
        update(tree[node].l, tree[pre].l, l, mid, x);
    else 
        update(tree[node].r, tree[pre].r, mid + 1, r, x);
}
int query(int u, int v, int l, int r, int k) {
    int ans, mid = l + r >> 1, x = tree[tree[v].l].sum - tree[tree[u].l].sum;
    if(l == r) 
        return l;
    if(x >= k) 
        ans = query(tree[u].l, tree[v].l, l, mid, k);
    else 
        ans = query(tree[u].r, tree[v].r, mid + 1, r, k - x);
    return ans;
}
int main() {
    scanf("%d%d", &n, &t);
    for(int i = 1; i <= n; i++)
        scanf("%d%d%d", &l[i], &r[i], &s[i]);
    for(int i = 1; i <= 2e5; i++) {
        val[++m] = inf;
        a[i].push_back(inf);
    }
    for(int i = 1; i <= t; i++) {
        int x;
        scanf("%d", &x);
        if(a[x][0] == inf)
            val[x] = a[x][0] = i;
        else {
            val[++m] = i;
            a[x].push_back(i);
        }
    }
    sort(val + 1, val + 1 + m);
    m = unique(val + 1, val + 1 + m) - val - 1;
    build(rt[0], 1, m);
    for(int i = 1; i <= 2e5; i++) {
        a[i][0] = lower_bound(val + 1, val + 1 + m, a[i][0]) - val;
        update(rt[i], rt[i - 1], 1, m, a[i][0]);
        for(int j = 1; j < a[i].size(); j++)
            update(rt[i], rt[i], 1, m, a[i][j]);
    }
    for(int i = 1; i <= n; i++) {
        int res = query(rt[l[i] - 1], rt[r[i]], 1, m, s[i]);
        if(val[res] != inf)
            ans[val[res]]++;
    }
    for(int i = 1; i <= t; i++)
        printf("%d\n", ans[i]);
    return 0;
}
posted @   Terac  阅读(6)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示