• LG 题解 P7424 [THUPC2017] 天天爱射击


    题目传送

    Solution

    经典整体二分,挺简单的一道题。

    容易想到,我们知道了每个木板在什么时候被击碎就知道哪颗子弹击碎的它。

    对于每个木板可以二分这个时刻,同时考虑所有木板就变成了整体二分。

    每颗子弹可以看做树状数组的单点修改。

    然后对于一个木板就可以看做查询一段区间被多少颗子弹打击。

    能击碎的分一组,不能击碎的分一组。

    然后这题套上整体二分的模板就做完了。

    不断分治下去,到 (l=r) 时直接统计答案即可。

    注意一种特殊情况,有的木板有可能自始至终都没有被击碎。

    这种情况很好处理,从 ([1,m+1]) 开始分治即可,这样自始至终都没有被击碎的木板会被划分到 (m+1) 的位置,这块贡献就不会被统计进去了。

    调这种题要时刻注意细节,样例弱的一匹,不行就自己捏几个。

    其他的看代码吧,有详细注释。

    Code

    /*
    Work by: Suzt_ilymics
    Problem: 不知名屑题
    Knowledge: 垃圾算法
    Time: O(能过)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #define LL long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 4e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    const int Max = 2e5;
    
    struct node {
        int l, r, k, type, id;
    }q[MAXN], q1[MAXN], q2[MAXN];
    
    int n, m, tot = 0;
    int ans[MAXN];
    int l[MAXN], r[MAXN], v[MAXN];
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    namespace BIT {
        int sum[MAXN];
        int lb(int x) { return x & -x; }
        void Modify(int x, int k) { for( ; x <= Max; x += lb(x)) sum[x] += k; }
        int Query(int x) { int res = 0; for( ; x; x -= lb(x)) res += sum[x]; return res; }
    }
    
    void Solve(int l, int r, int ql, int qr) { // 整体二分
        if(ql > qr) return ; // 这种空集直接返回即可。
        if(l == r) {
            for(int i = ql; i <= qr; ++i) if(q[i].type) ans[l]++;
            // 统计第 l 颗子弹击碎了几个木板。
            return ;
        }
        int mid = (l + r) >> 1, cnt1 = 0, cnt2 = 0;
        for(int i = ql; i <= qr; ++i) {
            if(q[i].type) {
                int sum = BIT::Query(q[i].r) - BIT::Query(q[i].l - 1);
                // 树状数组区间求和,表示 [l,mid] 区间的子弹打中了它几次。
                if(sum >= q[i].k) { // 打碎了分一组
                    q1[++cnt1] = q[i];
                } else { // 没打碎另分一组。
                    q[i].k -= sum; // 注意要把这次的贡献算上,以后就不用考虑了。
                    q2[++cnt2] = q[i];
                }
            } else {
                if(q[i].id <= mid) { 
                    BIT::Modify(q[i].l, 1);
                    q1[++cnt1] = q[i];
                } else {
                    q2[++cnt2] = q[i];
                }
            }
        }
        for(int i = 1; i <= cnt1; ++i) if(!q1[i].type) BIT::Modify(q1[i].l, -1); // 清空树状数组。
        for(int i = 1; i <= cnt1; ++i) q[ql + i - 1] = q1[i];
        for(int i = 1; i <= cnt2; ++i) q[ql + cnt1 + i - 1] = q2[i];
        Solve(l, mid, ql, ql + cnt1 - 1);
        Solve(mid + 1, r, ql + cnt1, ql + cnt1 + cnt2 - 1);
    }
    
    int main()
    {
        n = read(), m = read();
        for(int i = 1; i <= n; ++i) l[i] = read(), r[i] = read(), v[i] = read();
        // 注意因为我们子弹和木板共用一个数组 q,要先操作子弹,所以把木板放子弹的后边。
        for(int i = 1, x; i <= m; ++i) {
            x = read();
            q[++tot] = (node){x, 0, 0, 0, i};
        }
        for(int i = 1; i <= n; ++i) q[++tot] = (node){l[i], r[i], v[i], 1, i};
        Solve(1, m + 1, 1, tot);
        for(int i = 1; i <= m; ++i) printf("%d
    ", ans[i]);
        return 0;
    }
    
    
  • 相关阅读:
    Python3爬取前程无忧数据分析工作并存储到MySQL
    MySQL操作数据库和表的基本语句(DDL
    MyBatis的增删改查操作
    指定方向或者位置移动
    AI-Tank
    转载人家写的CURSOR
    Ajax学习整理笔记
    全面解析注解
    java调用存储过程mysql
    JAVA如何调用mysql写的存储过程
  • 原文地址:https://www.cnblogs.com/Silymtics/p/solution-P7424.html
Copyright © 2020-2023  润新知