zoukankan      html  css  js  c++  java
  • 线段树扫描线

    扫描线一般运用在图形上面,它和它的字面意思十分相似,就是一条线在整个图上扫来扫去,它一般被用来解决图形面积,周长等问题。     -------OI Wiki
    

    扫描线求面积并

    P5490 【模板】扫描线

    如图,假设三个矩形,求其并集的面积。考虑将每个矩形处理成两条平行于 y 轴的线段,左边的线段标记为 +1 ,右边的线段标记为 -1 ,用一个四元组((x, y_1, y_2, k))存下来,于是我们得到了这样一个图形:

    按照四元组中 x 坐标由小到大排序,我们就可以分成一段一段地处理整个图形:

    不同颜色表示一次处理的一段,此图形共可分成5段。
    考虑每次处理的矩形,沿 x 轴的长度很容易知道,即当前四元组 x 减去上一个四元组 x 。我们要处理沿 y 轴的长度,维护一个序列:
    1.若当前读到一个标记为 +1 的线段 ,就在序列上将这段区间 +1 ,反之 - 1。
    2.每次统计时求序列上标记大于 0 的长度即可。
    具体处理:
    1.由于题目中数据较大,需要离散化,用一个数组(num[])映射到原坐标值。
    2.用线段树维护序列(序列中第 i 项对应([num[i], num[i + 1] ])这个区间),线段树中每个节点维护两个值:s 和 cnt ,cnt 表示此段被标记的值,s 表示这段上 cnt > 0 的长度。update时,若当前结点 cnt > 0,那么 s 值为整段的长度,即(num[r + 1] - num[l]),否则为两个子结点长度之和,因为每次查询我们只关心整段序列,即线段树根结点,所以这个区间修改不需要标记下传,。
    3.读入一个四元组((x, y_1, y_2, k))时,先统计答案,再将序列上([y_1, y_2 - 1])这段的标记加上 k ,。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    using namespace std;
    typedef long long lld;
    const int N = 100005;
    const int M = 1000005;
    int n, tot = 0, num[M], maxn = 0;
    lld ans = 0;
    struct Tree {
    	int cnt, s;
    } e[M << 2];
    struct node {
    	int x, l, r, k;
    } p[N << 1];
    void update(int i, int l, int r) {
    	if(e[i].cnt > 0) e[i].s = num[r + 1] - num[l];// l, r表示num[l]到num[r + 1]这一段
    	else e[i].s = e[i << 1].s + e[i << 1 | 1].s;
    }
    void add(int i, int l, int r, int nl, int nr, int k) {
    	if(l >= nl && r <= nr) {
    		e[i].cnt += k; update(i, l, r);
    		return ;
    	}
    	int mid = (l + r) >> 1;
    	if(nl <= mid) add(i << 1, l, mid, nl, nr, k);
    	if(nr > mid) add(i << 1 | 1, mid + 1, r, nl, nr, k);
    	update(i, l, r);
    }
    int get() {
    	return e[1].s;
    }
    void add1(int x1, int x2, int y1, int y2) {
    	p[++tot].x = x1; p[tot].l = y1; p[tot].r = y2; p[tot].k = 1;
    	p[++tot].x = x2; p[tot].l = y1; p[tot].r = y2; p[tot].k = -1;
    	maxn = max(y1, max(y2, maxn));
    }
    int a[N], b[N], c[N], d[N];
    vector <int> tmp;
    bool cmp(node a, node b) {
    	return a.x < b.x;
    }
    int main() {
    //	freopen("data.in", "r", stdin);
    	scanf("%d", &n);
    	for(int i = 1, x1, x2, y1, y2; i <= n; i++) {
    		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
    		a[i] = x1, b[i] = x2, c[i] = y1, d[i] = y2;
    		tmp.push_back(y1); tmp.push_back(y2);
    	}
    	sort(tmp.begin(), tmp.end());//离散化
    	for(int i = 1; i <= n; i++) {
    		int save1 = c[i], save2 = d[i];
    		c[i] = lower_bound(tmp.begin(), tmp.end(), c[i]) - tmp.begin() + 1;
    		d[i] = lower_bound(tmp.begin(), tmp.end(), d[i]) - tmp.begin() + 1;
    		num[c[i]] = save1, num[d[i]] = save2;//映射原值
    		add1(a[i], b[i], c[i], d[i]);//添加四元组
    	}
    	sort(p + 1, p + 1 + tot, cmp);//按照x排序
    	add(1, 1, maxn, p[1].l, p[1].r - 1, p[1].k);//添加第一条线段
    	for(int i = 2; i <= tot; i++) {
    		ans = ans + (1ll * get() * (p[i].x - p[i - 1].x));//统计答案,长乘宽
    		add(1, 1, maxn, p[i].l, p[i].r - 1, p[i].k);//修改序列
    	}
    	printf("%lld", ans);
    	return 0;
    }
    

    扫描线求矩形周长

    P1856 [USACO5.5]矩形周长Picture

    方法类似求面积,离散化处理,线段树区间修改和查询方法同上。
    统计答案: 读入一个四元组时,先记录当前的查询的值,进行修改后再查询一次,答案增加的量为两次查询的差的绝对值。 证明方法显然,这里不多bb。
    细节:
    1.这样每次只能统计平行于 y 轴的线段的周长和,我们需要将每个点的 x,y 坐标交换后再重复上述操作。
    2.四元组排序时若 x 坐标相同,则 k = 1 的排在前面(先加上再减去对统计对答案没有影响,若是先减去在加上可能会导致答案偏大)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int N = 5005;
    const int M = 20015;
    const int F = 10001;
    int a[N], b[N], c[N], d[N], n, tot = 0;
    int num[M << 2];
    long long ans = 0;
    struct node {
    	int x, l, r, k;
    } p[N << 1];
    void add(int x1, int y1, int x2, int y2) {
    	if(y1 > y2) swap(y1, y2);
    	p[++tot].x = x1, p[tot].l = y1, p[tot].r = y2, p[tot].k = 1;
    	p[++tot].x = x2, p[tot].l = y1, p[tot].r = y2, p[tot].k = -1;
    }
    struct tree {
    	int s, cnt;
    } e[M << 2];
    vector <int> tmp, opt;
    void update(int i, int l, int r) {
    	if(e[i].cnt > 0) e[i].s = num[r + 1] - num[l];
    	else e[i].s = e[i << 1].s + e[i << 1 | 1].s;
    }
    void add(int i, int l, int r, int nl, int nr, int k) {
    	if(l >= nl && r <= nr) {
    		e[i].cnt += k; update(i, l, r);
    		return ;
    	}
    	int mid = (l + r) >> 1;
    	if(nl <= mid) add(i << 1, l, mid, nl, nr, k);
    	if(nr > mid) add(i << 1 | 1, mid + 1, r, nl, nr, k);
    	update(i, l, r);
    }
    int get() {
    	return e[1].s;
    }
    bool cmp(node a, node b) {
    	if(a.x == b.x) return a.k > b.k; //k = 1排在前面
    	return a.x < b.x;
    }
    void solve() {
    	sort(p + 1, p + 1 + tot, cmp);
    	for(int i = 1; i <= tot; i++) {
    		long long per = get();
    		add(1, 1, tot, p[i].l, p[i].r - 1, p[i].k);
    		ans = (ans + abs(per - get()));
    	}
    }
    int main() {
    //	freopen("data.in", "r", stdin);
    	scanf("%d", &n);
    	for(int i = 1, x1, x2, y1, y2; i <= n; i++) {
    		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
    		a[i] = x1, b[i] = y1, c[i] = x2, d[i] = y2;
    		tmp.push_back(y1); tmp.push_back(y2);
    		opt.push_back(x1), opt.push_back(x2);
    	}
    	sort(tmp.begin(), tmp.end());
    	for(int i = 1; i <= n; i++) {
    		int save1 = b[i], save2 = d[i];
    		b[i] = lower_bound(tmp.begin(), tmp.end(), b[i]) - tmp.begin() + 1;
    		d[i] = lower_bound(tmp.begin(), tmp.end(), d[i]) - tmp.begin() + 1;
    		num[b[i]] = save1, num[d[i]] = save2;
    		add(a[i], b[i], c[i], d[i]);
    	}
    	solve();
    	for(int i = 0; i < (M << 2); i++) e[i].s = e[i].cnt = 0;
    	memset(num, 0, sizeof(num));
    	tot = 0;
    	sort(opt.begin(), opt.end());
    	for(int i = 1; i <= n; i++) {
    		int save1 = a[i], save2 = c[i];
    		a[i] = lower_bound(opt.begin(), opt.end(), a[i]) - opt.begin() + 1;
    		c[i] = lower_bound(opt.begin(), opt.end(), c[i]) - opt.begin() + 1;
    		num[a[i]] = save1, num[c[i]] = save2;
    		add(b[i], a[i], d[i], c[i]);
    	}
    	solve();
    	printf("%lld", ans);
    	return 0;
    }
    
  • 相关阅读:
    Applet
    Tutorial中代码的区别及不同效果
    Session
    代码解析&Filter用户授权例子
    Web的后台运作过程
    XML
    数据库
    spring常用注解
    spring mvc 常用注解
    @requestMapping
  • 原文地址:https://www.cnblogs.com/mcggvc/p/12538960.html
Copyright © 2011-2022 走看看