BAPC 2019 G. Gluttonous Goop 線段樹掃描線

題目鏈接:https://nanti.jisuanke.com/t/44323
題意:有若干個小方塊,每秒會往八個方向擴展,問k秒後有多少個區域被覆蓋
思路:k秒後每個格子都擴展成了一個矩形,所以也就是求矩形面積並,用掃描線很好解決。 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define fi first
#define se second
#define ls rt << 1
#define rs rt << 1|1
#define lson l, mid, ls
#define rson mid + 1, r, rs
#define pll pair<ll, ll>
#define pii pair<int, int>
#define ull unsigned long long
#define pdd pair<double, double>
const int mod = 1e9 + 7;
const int maxn = 1e4 + 10;
const int inf = 0x3f3f3f3f;
struct node
{
    int x, y1, y2, k;
    bool operator < (const node &r) const
    {
        return x < r.x;
    }
}a[maxn << 3];
int cnt[maxn << 3], len[maxn << 3];
vector<int> v;
char s[30][30];

int getid(int x)
{
    return lower_bound(v.begin(), v.end(), x) - v.begin();
}
void push_down(int l, int r, int rt)
{   //例如[4, 4]這種情況,就要v[5] - v[4],所以要r+1
    if(cnt[rt]) len[rt] = v[r + 1] - v[l]; //被覆蓋的話該節點的長度就爲兩個端點間的長度
    else len[rt] = len[ls] + len[rs]; //否則就統計子區間的長度
}
void update(int ql, int qr, int val, int l, int r, int rt)
{
    //cout << ql << " " << qr << endl;
    if(ql <= l && r <= qr)
    {
        cnt[rt] += val;
        push_down(l, r, rt);
        return;
    }
    int mid = (l + r) >> 1;
    if(ql <= mid) update(ql, qr, val, lson);
    if(qr > mid) update(ql, qr, val, rson);
    push_down(l, r, rt);
}

int main()
{
    int r, c, k, tot = 0;
    scanf("%d%d%d", &r, &c, &k);
    for(int i = r; i >= 1; --i)
    {
        scanf("%s", s[i] + 1);
        for(int j = 1; j <= c; ++j)
        {
            if(s[i][j] == '#')
            {
                int x1 = i - k, y1 = j - k, x2 = i + k + 1, y2 = j + k + 1;
                a[++tot].x = x1, a[tot].y1 = y1, a[tot].y2 = y2, a[tot].k = 1;
                a[++tot].x = x2, a[tot].y1 = y1, a[tot].y2 = y2, a[tot].k = -1;
                v.push_back(y1), v.push_back(y2);
            }
        }
    }
    sort(a + 1, a + tot + 1);
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    ll ans = 0;
    int sz = v.size(); //這纔是線段樹的範圍
    for(int i = 1; i < tot; ++i)
    {
        int l = getid(a[i].y1), r = getid(a[i].y2) - 1; //區間[l, r);
        //cout << l << " " << r << endl;
        update(l, r, a[i].k, 0, sz - 1, 1);
        // cout << l << " " << r << endl;
        ans += 1ll * len[1] * (a[i + 1].x - a[i].x);
    }
    printf("%lld\n", ans);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章