zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", ans + n);
        return 0;
    }
    
  • 相关阅读:
    Linux命令格式及7个常见终端命令
    Linux主要目录速查表
    Linux和Windows系统目录结构区别
    C语言下进制的使用
    C语言变量和常量
    C语言的关键字和数据类型
    Linux下交换文件说明
    gcc编译过程
    C语言图形界面QT和MFC(待学)
    字符编码问题
  • 原文地址:https://www.cnblogs.com/whenc/p/13877906.html
Copyright © 2011-2022 走看看