1. 程式人生 > 其它 >LG 題解 P7424 [THUPC2017] 天天愛射擊

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\n", ans[i]);
    return 0;
}