1. 程式人生 > 實用技巧 >Luogu5816 內部白點(掃描線)題解

Luogu5816 內部白點(掃描線)題解

題意

有一張由黑白點構成的網格圖,給出一些黑點的座標,求上下左右都有黑點(不必相鄰)的白點數目與原來黑點數目之和。

一個說明

為什麼原題面中永不終止的情況是不可能的?

對於每一個將會被染成黑色的白點,它的上方,下方,左邊,右邊(不必相鄰)一定是都是有黑點的,而一個既不在所屬列的兩端,又不在所屬行的兩端的黑點(即它的上下左右(不必相鄰)都有黑點),是不會對答案有任何貢獻的。換言之,任何被染成黑點的白點,都不會再染別的白點,所以整個染色只會進行一輪。

演算法

一個不像掃描線的掃描線

不會掃描線的戳我

思路

只有處於同一行的兩黑點之間的部分才能染色,為了方便,我們可以只處理相鄰兩黑點。

先將黑點以\(y\)

為第一關鍵字,\(x\)為第二關鍵字排序,對於每一列,處理出最高點與最低點,當處理到最高點時線上段樹上將該列\(+1\), 最低點時最線段樹上將該列\(-1\)。對於每兩個同行相鄰的點,區間求和即可。

程式碼

#include <cstdio>
#include <algorithm>

using namespace std;

const int maxn = 5e5 + 10;
int b[maxn],n,ans;
struct Mar{
    int x,y;
}m[maxn];
struct Line{
    int high,low;
    Line(){high = 0, low = 0x3f3f3f3f;}
}ll[maxn];

struct Seg_Tree{
    #define lc(x) x << 1
    #define rc(x) x << 1 | 1
    int c[maxn << 2],tag[maxn << 2];
    
    void f(int l, int r, int p, int x){
        c[p] += (r - l + 1) * x;
        tag[p] = x;
    }
    
    void downdate(int l, int r, int p){
        if(tag[p]){
            int mid = (l + r) >> 1;
            f(l, mid, lc(p), tag[p]);
            f(mid + 1, r, rc(p), tag[p]);
            tag[p] = 0;
        }
    }
    
    void add(int L, int R, int l, int r, int p, int x){
        if(L <= l && R >= r){
            f(l, r, p, x);
            return;
        }
        downdate(l, r, p);
        int mid = (l + r) >> 1;
        if(mid >= L) add(L, R, l, mid, lc(p), x);
        if(mid < R) add(L, R, mid + 1, r, rc(p), x);
        c[p] = c[lc(p)] + c[rc(p)];
    }
    
    int query(int L, int R, int l, int r, int p){
        if(L <= l && R >= r){
            return c[p];
        }
        downdate(l, r, p);
        int mid = (l + r) >> 1, sum = 0;
        if(mid >= L) sum += query(L, R, l, mid, lc(p));
        if(mid < R) sum += query(L, R, mid + 1, r, rc(p));
        return sum;
    }
}tree;

bool cmp(Mar x, Mar y){return x.y == y.y ? x.x < y.x : x.y > y.y;}

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d %d",  &m[i].x, &m[i].y);
        b[i] = m[i].x; b[i + n] = m[i].y;
    } sort(b + 1, b + 1 + 2 * n);

    int _n = unique(b + 1, b + 1 + 2 * n) - b - 1;
    for(int i = 1; i <= n; ++ i){
        int pos1 = lower_bound(b + 1, b + 1 + _n, m[i].x) - b;
        int pos2 = lower_bound(b + 1, b + 1 + _n, m[i].y) - b;
        m[i].x = pos1, m[i].y = pos2;
        //printf("%d %d\n", pos1, pos2);
        ll[m[i].x].high = max(ll[m[i].x].high, m[i].y);
        ll[m[i].x].low = min(ll[m[i].x].low, m[i].y);
    } sort(m + 1, m + 1 + n, cmp);
    
    m[n + 1].y = 0x3f3f3f3f;
    for(int i = 1; i <= n; ++ i){
        if(m[i].y == ll[m[i].x].high) tree.add(m[i].x, m[i].x, 1, _n, 1, 1);
        if(m[i].y == ll[m[i].x].low) tree.add(m[i].x, m[i].x, 1, _n, 1, -1);
        if(m[i + 1].y == m[i].y) ans += tree.query(m[i].x + 1, m[i + 1].x - 1, 1, _n, 1);
    } printf("%d\n", ans + n);
    return 0;
}