• P5490 【模板】扫描线


    扫描线的过程不做多述
    主要讲解代码实现,没有深刻理解是无法完全打出来的
    首先离散化X坐标,因为我们要用线段树进行维护,x坐标过大,数组范围是不允许的
    再考虑线段树怎么维护
    假如维护k[1,3]--->两个子节点k1[1,2],k2[3,3]
    假如k1的len=X[2]-X[1]
    那k2的len=X[3]-x[3]
    k2此时为一个点了,明显不符合
    所以我们设维护k[l,r],维护的是一个区间[l,r+1]
    这样就巧妙地解决了
    因为每次我们要查询1号节点的总长度,所以我们有个pushup操作,懒标记就不用pushdown
    code by std

    #include <stdio.h>
    #include <iostream>
    #include <algorithm>
    #define lson (x << 1)
    #define rson (x << 1 | 1)
    using namespace std;
    const int MAXN = 1e6 + 10;
    typedef long long ll;
    
    int n, cnt = 0;
    ll x1, y1, x2, y2, X[MAXN << 1];
    
    struct ScanLine {
    	ll l, r, h;
    	int mark;
    //  mark用于保存权值 (1 / -1)
    	bool operator < (const ScanLine &rhs) const {
    		return h < rhs.h;
    	}
    } line[MAXN << 1];
    
    struct SegTree {
    	int l, r, sum;
    	ll len;
    //  sum: 被完全覆盖的次数;
    //  len: 区间内被截的长度。
    } tree[MAXN << 2];
    
    void build_tree(int x, int l, int r) {
    //  我觉得最不容易写错的一种建树方法
    	tree[x].l = l, tree[x].r = r;
    	tree[x].len = 0;
    	tree[x].sum = 0;
    	if(l == r)
    		return;
    	int mid = (l + r) >> 1;
    	build_tree(lson, l, mid);
    	build_tree(rson, mid + 1, r);
    	return;
    }
    
    void pushup(int x) {
    	int l = tree[x].l, r = tree[x].r;
    	if(tree[x].sum /* 也就是说被覆盖过 */ )
    		tree[x].len = X[r + 1] - X[l];
    //      更新长度        
    	else
    		tree[x].len = tree[lson].len + tree[rson].len;
    //      合并儿子信息
    }
    
    void edit_tree(int x, ll L, ll R, int c) {
    	int l = tree[x].l, r = tree[x].r;
    //  注意,l、r和L、R的意义完全不同
    //  l、r表示这个节点管辖的下标范围
    //  而L、R则表示需要修改的真实区间
    	if(X[r + 1] <= L || R <= X[l])
    		return;
    //  这里加等号的原因:
    //  假设现在考虑 [2,5], [5,8] 两条线段,要修改 [1,5] 区间的sum
    //  很明显,虽然5在这个区间内,[5,8] 却并不是我们希望修改的线段
    //  所以总结一下,就加上了等号
    	if(L <= X[l] && X[r + 1] <= R) {
    		tree[x].sum += c;
    		pushup(x);
    		return;
    	}
    	edit_tree(lson, L, R, c);
    	edit_tree(rson, L, R, c);
    	pushup(x);
    }
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; i++) {
    		scanf("%lli %lli %lli %lli", &x1, &y1, &x2, &y2);
    		X[2 * i - 1] = x1, X[2 * i] = x2;
    		line[2 * i - 1] = (ScanLine) {x1, x2, y1, 1};
    		line[2 * i] = (ScanLine) {x1, x2, y2, -1};
    //      一条线段含两个端点,一个矩形的上下边都需要扫描线扫过
    	}
    	n <<= 1;
    //  直接把 n <<= 1 方便操作
    	sort(line + 1, line + n + 1);
    	sort(X + 1, X + n + 1);
    	int tot = unique(X + 1, X + n + 1) - X - 1;
    //  去重最简单的方法:使用unique!(在<algorithm>库中)
    	build_tree(1, 1, tot - 1);
    //  为什么是 tot - 1 :
    //  因为右端点的对应关系已经被篡改了嘛…
    //  [1, tot - 1]描述的就是[X[1], X[tot]]
    	ll ans = 0;
    	for(int i = 1; i < n /* 最后一条边是不用管的 */ ; i++) {
    		edit_tree(1, line[i].l, line[i].r, line[i].mark);
    //      先把扫描线信息导入线段树
    		ans += tree[1].len * (line[i + 1].h - line[i].h);
    //      然后统计面积
    	}
    	printf("%lli", ans);
    	return 0;
    }
  • 相关阅读:
    食物链(并查集)
    字母奔跑
    19+199+1999+……+1999…9(1999个9)和是多少?
    利用union判断CPU是大端模式还是小端模式
    闰年个数(非循环)
    计算catlan数f(n)(动态规划)
    模拟循环调度(队列)
    回溯表达式,+不小于4个,乘号不小于2个,列出表达式种数
    枚举{1,2,3,4}子集
    回溯n个元素的子集
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/15947646.html
Copyright © 2020-2023  润新知