扫描线

http://www.cnblogs.com/Konjakmoyu/p/6050343.html

关于扫描线的理解

https://vjudge.net/contest/242515#problem/F

 Atlantis

 HDU - 1542 

这个题目是求总的矩形覆盖面积。线段树中的点表示的是一段区间的长度。线段按照纵坐标由小到大来更新进线段树。

线段树的结构体中有个lazy标记与长度标记,lazy标记表示这个区间段被覆盖了几次,每次只要更新相对应区间的lazy值,不需要push_down 操作,然后push_up操作是来更新长度值,如果lazy标记为1,则表示该区间

有效,如果是0就表示不能确定,只能由其左右孩子的值来确定,(这里体现为什么不需要push_down操作,因为不一定要递归到叶子节点来找长度,可以直接拿一个已经标记了的区间来表示长度,如果标记为0表示没有被标记过或者先前的1被抵消掉了(在纸上画一下),那么就要通过子节点来得到长度。)

 

//#include<bits/stdc++.h>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxm = 105;
struct seg {
    double x1, x2, h;
    int fl;
//    seg(double x1 = 0, double x2 = 0, double h = 0, int fl = 0) : x1(x1), x2(x2), h(h), fl(fl) {};
    bool operator < (const struct seg &a) const {
        return h < a.h;
    }
}seg[maxm * 2];

struct Node {
    int l, r;
    int mid() {
        return (l + r) >> 1;
    }
}node[maxm << 3];
int lazy[maxm << 3];
double sum[maxm << 3];
double xx[maxm * 2];
int n;

void build(int ri, int l, int r) {
    node[ri].l = l;
    node[ri].r = r;
    lazy[ri] = 0;
    sum[ri] = 0;
    if(l == r) return;
    int m = node[ri].mid();
    build(ri << 1, l, m);
    build(ri << 1 | 1, m + 1, r);
}

void push_up(int ri) {
    if(lazy[ri]) {
        sum[ri] = xx[node[ri].r + 1] - xx[node[ri].l ];
        return;
    }
    if(node[ri].l == node[ri].r) {
        sum[ri] = 0;
        return;
    }//对于叶子节点还是要特殊考虑的,因为1-n之外的并没有初始化。
    sum[ri] = sum[ri << 1] + sum[ri << 1 | 1];
}

void update(int ri, int l, int r, int flag) {
    if(node[ri].l >= l && node[ri].r <= r) {
        lazy[ri] += flag;
        push_up(ri);
        return;
    }
    int m = node[ri].mid();
    if(r <= m) update(ri << 1, l, r, flag);
    else if(l > m) update(ri << 1 | 1, l, r, flag);
    else {
        update(ri << 1, l, m, flag);
        update(ri << 1 | 1, m + 1, r, flag);
    }
    push_up(ri);
}

int main() {
    double x1, x2, y1, y2;
    int ca = 0;
    while(~scanf("%d", &n) && n) {
        for(int i = 1; i <= n; i++) {
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
            seg[2 * i - 1] = (struct seg) {x1, x2, y1, 1};
            xx[2 * i - 1] = x1;
            seg[2 * i] = (struct seg) {x1, x2, y2, -1};
            xx[2 * i] = x2;
        }
        sort(seg + 1, seg + 2 * n + 1);
        sort(xx + 1, xx + 2 * n + 1);
        int k = unique(xx + 1, xx + 2 * n + 1) - xx;
        build(1, 1, k - 1);
        double res = 0;
        for(int i = 1; i <= 2 * n; i++) {
            int l = lower_bound(xx + 1, xx + k, seg[i].x1) - xx;
            int r = lower_bound(xx + 1, xx + k, seg[i].x2) - xx - 1;
            if(l <= r) {
//                printf("nas
");
                update(1, l, r, seg[i].fl);
            }
            res += sum[1] * (seg[i + 1].h - seg[i].h);
        }
//        for(int i = 1; i <= 3; i++) printf("%.2f
", len[i]);
        printf("Test case #%d
Total explored area: %.2f

", ++ca, res);
    }
    return 0;
}

 

http://acm.hdu.edu.cn/showproblem.php?pid=1255

求覆盖两次的矩形面积。

https://blog.csdn.net/codeswarrior/article/details/81081692 跟覆盖一次的差不多,只是结构体中长度的变为一个至少覆盖一次与至少覆盖两次的长度。

这题的代码在一般的线段树求面积并的基础上进行了修改,但是所用的思想是一样的,所以不难理解

回忆一下一般的求矩形覆盖面积,线段树节点里面有一个重要的变量,cnt。这个变量表示了该节点表示的区间被完全覆盖,如果cnt=0,说明没有被完全覆盖(但不代表没有被覆盖),要算出该节点所代表的区间被覆盖的长度,需要由它左右孩子节点被覆盖的长度相加所得。如果cnt=1,表示被完全覆盖,覆盖长度就是该区间长度。如果cnt>1说明也是被完全覆盖,不过不止覆盖了一次,在算覆盖长度的时候,和cnt=1的计算方法是一样的。注意一点,节点里还有另一个变量len,就是该区间被覆盖的长度,但是我们注意一下,这个len准确的意义应该是,被覆盖了一次或以上的长度,只是这个意义在一般的求面积问题中,不需要过分强调

而在这题中我们要计算被覆盖两次或以上的部分面积,我们在线段树节点中增设了一个变量,ss,其中s表示该该区间内被覆盖了1次或以上的长度,ss表示被覆盖了2次或以上的长度

我们是怎么计算最后的面积的?一样的道理,从下往上扫描矩形,每次添加一条矩形上下边,然后看看t[1].ss是多少,再乘上高度差。因为t[1]表示了总区间,而ss表示被覆盖两次或以上的长度,即计算时我们忽略掉只被覆盖一次的长度

问题的关键变为怎么计算一个节点的ss

分情况讨论

1.cnt>1 : 说明该区间被覆盖两次或以上,那么长度就可以直接计算,就是该区间的长度

剩下的情况就是cnt=1或cnt=0

2.先看叶子节点,因为是叶子没有孩子了,所以被覆盖两次货以上的长度就是0(无论cnt=1或cnt=0都是0,因为是叶子。。。)

3.不是叶子节点 ,且cnt=1.注意这里,cnt=1确切的意义是什么,应该是,可以确定,这个区间被完全覆盖了1次,而有没有被完全覆盖两次或以上则不知道无法确定,那么怎么怎么办了,只要加上t[lch].s + t[rch].s  即,看看左右孩子区间被覆盖了一次或以上的长度,那么叠加在双亲上就是双亲被覆盖两次或以上的长度

3.不是叶子节点,且cnt=0,确切的意义应该是不完全不知道被覆盖的情况(不知道有没有被覆盖,被覆盖了几次,长度是多少都不知道),这种情况,只能由其左右孩子的信息所得

t[lch].ss + t[rch].ss  , 即直接将左右孩子给覆盖了两次或以上的长度加起来,这样才能做到不重不漏

注意!!始终是下边为1,上边-1,因为只有这样,标记覆盖次数cnt才能是正的,题目题意有问题和输入样例不符,按照输入样例为准

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define ls i<<1
#define rs i<<1|1
#define m(i) ((q[i].l+q[i].r)>>1)
typedef long long ll;
const int N = 1004;
struct Edge{//扫描线
    double l,r;
    double h;
    int f;
    bool operator < (const Edge &a)const{
        return h < a.h;
    }
}e[N<<1];
struct Node{
    int l,r;
    int s;//该区间到覆盖状态
    double len1;//覆盖1次及以上的长度
    double len2;//覆盖2次及以上的长度
}q[(2*N)<<2];
double x[N<<1];
void pushup(int i){
    if(q[i].s){
        q[i].len1 = x[q[i].r+1] - x[q[i].l];
    }
    else if(q[i].l == q[i].r){
        q[i].len1 = 0;
    }
    else{
        q[i].len1 = q[ls].len1 + q[rs].len1;
    }
    //下面是不同于1次覆盖的部分
    if(q[i].s > 1){//该区间完全覆盖2次及以上
        q[i].len2 = x[q[i].r+1] - x[q[i].l];
    }
    else if(q[i].l == q[i].r){
        q[i].len2 = 0;
    }
    else if(q[i].s == 1){
        //这里可能需要理解一下,s=1说明这个区间要被
        //完整覆盖,但是之前子区间已经有的被覆盖了1次
        //这个时候我们只需要找这个区间左右子区间覆盖1次的就行了,加上这次完全覆盖就是2次了
        q[i].len2 = q[ls].len1 + q[rs].len1;
    }
    else{
        //如果覆盖0次那么只能去子区间内找覆盖两次的了
        q[i].len2 = q[ls].len2 + q[rs].len2;
    }
}
void build(int i,int l,int r){
    q[i].l = l;
    q[i].r = r;
    q[i].s = q[i].len1 = q[i].len2 = 0;
    if(l == r) return;
    int mid = m(i);
    build(ls,l,mid);
    build(rs,mid+1,r);
}
void update(int i,int l,int r,int f){
    if(l == q[i].l && q[i].r == r){
        q[i].s += f;
        pushup(i);
        return;
    }
    int mid = m(i);
    if(r <= mid) update(ls,l,r,f);
    else if(l > mid) update(rs,l,r,f);
    else{
        update(ls,l,mid,f);
        update(rs,mid+1,r,f);
    }
    pushup(i);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        int tot = 0;
        double x1,x2,y1,y2;
        for(int i = 1; i <= n; i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            Edge &t1 = e[tot];
            Edge &t2 = e[tot+1];
            t1.l = t2.l = x1;
            t1.r = t2.r = x2;
            t1.h = y1;
            t1.f = 1;
            t2.h = y2;
            t2.f = -1;
            x[tot] = x1;
            x[tot+1] = x2;
            tot += 2;
        }
        sort(x,x+tot);
        sort(e,e+tot);
        int k = 1;
        for(int i = 1; i < tot; i++){
            if(x[i] != x[i-1])
                x[k++] = x[i];
        }
        build(1,0,k-1);
        double ans = 0.0;
        for(int i = 0; i < tot; i++){
            int l = lower_bound(x,x+k,e[i].l) - x;
            int r = lower_bound(x,x+k,e[i].r) - x -1;
            update(1,l,r,e[i].f);
            ans += q[1].len2 * (e[i+1].h - e[i].h);
        }
        printf("%.2f
",ans);
    }
    return 0;
}

http://acm.csu.edu.cn:20080/csuoj/problemset/problem?pid=1082

求被最多个矩形覆盖的区域次数,然后要如果一个y值有多条线,应该先让正值的先进去,然后再试负值的,保证最大。

一个lazy标记和max数组,不往下更新,首先x轴上任何一个区域在更新的过程中肯定不可能达到负数,

然后lazy标记是指整个区间要加的值,max是指这个区间的最大值,因为没有往下更新,所以某个区间的最大值就是其左右孩子的最大值加上直接修改整个区间的lazy值,

因为这个题目是求祖先节点的最大值,所以可以这样做。

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 2e4 + 5;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define root 0,10000,1

int MAX[MX << 2], col[MX << 2];

struct Que {
    int L, R, top, d;
    bool operator<(const Que &b)const {
        if(top == b.top) return d > b.d;
        return top < b.top;
    }
    Que(int _top = 0, int _L = 0, int _R = 0, int _d = 0) {
        L = _L; R = _R; top = _top; d = _d;
    }
} Q[MX];

void push_up(int rt) {
    MAX[rt] = max(MAX[rt << 1], MAX[rt << 1 | 1])+ col[rt];
}

//void push_down(int rt) {
//    if(col[rt]) {
//        col[rt << 1] += col[rt];
//        col[rt << 1 | 1] += col[rt];
//        MAX[rt << 1] += col[rt];
//        MAX[rt << 1 | 1] += col[rt];
//        col[rt] = 0;
//    }
//}

void update(int L, int R, int d, int l, int r, int rt) {
    if(L <= l && r <= R) {
        MAX[rt] += d;
        col[rt] += d;
     
return; } int m = (l + r) >> 1; // push_down(rt); if(L <= m) update(L, R, d, lson); if(R > m) update(L, R, d, rson); push_up(rt); } int main() { int n; //freopen("input.txt", "r", stdin); while(~scanf("%d", &n)) { memset(MAX, 0, sizeof(MAX)); memset(col, 0, sizeof(col)); for(int i = 1; i <= n; i++) { int x1, x2, y1, y2; scanf("%d%d%d%d", &x1, &x2, &y1, &y2); Q[i] = Que(y1, x1, x2, 1); Q[i + n] = Que(y2, x1, x2, -1); } sort(Q + 1, Q + 1 + 2 * n); int ans = 0; for(int i = 1; i <= 2 * n; i++) { update(Q[i].L, Q[i].R, Q[i].d, root); ans = max(ans, MAX[1]); } printf("%d ", ans); } return 0; }

https://vjudge.net/contest/285943#status/xiayuyang/N/0/

https://vjudge.net/problem/POJ-2482 天上有很多星星,求一个窗户能框住的最多的星星。

扫描线求横坐标的最小覆盖次数,因为是边框无效,所以在线段排序时让负值的排在前面,就可以保证平行于x轴方向的边框不算,然后因为线段树维护的区间是左闭右开,所以说右边界自然去掉了,

更上一题差不多,左闭右开相当于左边框实际小于左边界,但很接近,右边框实际小于右边界,但很接近,这样就相当于把左右两边界去掉了,至于上下边界就是先负值,后正值。

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int maxm = 2e4 + 5;
ll xx[maxm << 2];
ll ma[maxm << 3], sum[maxm << 3];

struct Seg {
    ll x1, x2, h, cnt;
    bool operator < (const struct Seg &a) {
        if(h == a.h) return cnt < a.cnt;
        return h < a.h;
    }
    Seg(ll x1 = 0, ll x2 = 0, ll h = 0, ll cnt = 0) : x1(x1), x2(x2), h(h), cnt(cnt) {};
} seg[maxm << 2];

struct Node {
    int l, r;
    int mid() {
        return (l + r) >> 1;
    }
} node[maxm << 3];

void build(int ri, int l, int r) {
    node[ri].l = l;
    node[ri].r = r;
    ma[ri] = 0;
    sum[ri] = 0;
    if(l == r) return;
    int m = node[ri].mid();
    build(ri << 1, l, m);
    build(ri << 1 | 1, m + 1, r);
}

void push_up(int ri) {
    ma[ri] = max(ma[ri << 1], ma[ri << 1 | 1]) + sum[ri];
}

void update(int ri, int l, int r, int val) {
    if(node[ri].l == l && node[ri].r == r) {
        sum[ri] += val;
        ma[ri] += val;
        return;
    }
    int m = node[ri].mid();
    if(r <= m) update(ri << 1, l, r, val);
    else if(l > m) update(ri << 1 | 1, l, r, val);
    else {
        update(ri << 1, l, m, val);
        update(ri << 1 | 1, m + 1, r, val);
    }
    push_up(ri);
}


int n;
ll x, y, c, w, h;
int main() {
    while(~scanf("%d%lld%lld", &n, &w, &h)) {
        int m = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%lld%lld%lld", &x, &y, &c);
            seg[2 * i - 1] = Seg(x - 1, x + w - 1, y, c);
            seg[2 * i] = Seg(x - 1, x + w - 1, y + h, -c);
            xx[++m] = x - 1;
            xx[++m] = x;
            xx[++m] = x + w - 1;
        }
        sort(seg + 1, seg + 2 * n + 1);
        sort(xx + 1, xx + m + 1);
        int k = unique(xx + 1, xx + m + 1) - xx;
        build(1, 1, k - 1);
        ll res = 0;
        for(int i = 1; i <= 2 * n; i++) {
            int l = lower_bound(xx + 1, xx + k, seg[i].x1 + 1) - xx;
            int r = lower_bound(xx + 1, xx + k, seg[i].x2 + 1) - xx - 1;
            if(l <= r) update(1, l, r, seg[i].cnt);
            res = max(res, ma[1]);
        }
        printf("%lld
", res);
    }
    return 0;
}

求周长

https://vjudge.net/contest/242515#problem/K

#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
 
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define root 0,20004,1
 
int const MP = 10000 + 5;
int const MX = 2e4 + 5;
 
int cnt[MX << 2];
int S[MX << 2];
 
struct Que {
    int d;
    int top, L, R;
    Que() {}
    Que(int _top, int _L, int _R, int _d) {
        top = _top; L = _L; R = _R; d = _d;
    }
    bool operator<(const Que &b)const {
        return top < b.top;
    }
} A[MX], B[MX];
 
void push_up(int l, int r, int rt) {
    if(cnt[rt]) S[rt] = r + 1 - l;
    else if(l == r) S[rt] = 0;
    else S[rt] = S[rt << 1] + S[rt << 1 | 1];
}
 
void update(int L, int R, int d, int l, int r, int rt) {
    if(L <= l && r <= R) {
        cnt[rt] += d;
        push_up(l, r, rt);
        return;
    }
 
    int m = (l + r) >> 1;
    if(L <= m) update(L, R, d, lson);
    if(R > m) update(L, R, d, rson);
    push_up(l, r, rt);
}
 
int main() {
    int n, ansk = 0;
    //freopen("input.txt", "r", stdin);
    while(~scanf("%d", &n)) {
        memset(cnt, 0, sizeof(cnt));
        memset(S, 0, sizeof(S));
 
        for(int i = 1; i <= n; i++) {
            int x1, y1, x2, y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            x1 += MP; y1 += MP; x2 += MP; y2 += MP;
            A[i] = Que(y1, x1, x2, 1);
            A[i + n] = Que(y2, x1, x2, -1);
            B[i] = Que(x1, y1, y2, 1);
            B[i + n] = Que(x2, y1, y2, -1);
        }
        sort(A + 1, A + 1 + 2 * n);
        sort(B + 1, B + 1 + 2 * n);
 
        int ans = 0, last = 0;
        for(int i = 1; i <= 2 * n; i++) {
            update(A[i].L, A[i].R - 1, A[i].d, root);
            ans += abs(last - S[1]);
            last = S[1];
        }
 
        last = 0;
        memset(cnt, 0, sizeof(cnt));
        memset(S, 0, sizeof(S));
 
        for(int i = 1; i <= 2 * n; i++) {
            update(B[i].L, B[i].R - 1, B[i].d, root);
            ans += abs(last - S[1]);
            last = S[1];
        }
 
        printf("%d
", ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/downrainsun/p/10698391.html