扫描线+线段树学习笔记

如果仔细观察扫描仪工作就会发现,扫描仪扫描时是一条线从头到尾扫一遍成像。这个算法形象化表示也是如此。

首先是扫描线板子题:矩形面积并。

题意:在平面直角坐标系中,给出若干个矩形,求所有矩形的面积并。

太长不看版:对于所有矩形的端点按照纵坐标排序,然后依次扫描矩形每加入一条线段覆盖,线段树查询所有区间中覆盖的长度即可。 

详细来说,就是我们对每个矩形进行抽象化处理,把矩形的水平两条边单独拿出来,记录这条边的左右端点和高度。并且给这条边赋上一个特殊的值,为+1或-1,对于矩形下方的边赋+1,上方的边赋-1,表示扫描过程中进入和离开矩形。然后用一个线段树维护区间内的线段长度,再乘上对应高度就行。

然后来一道需要稍稍构造一下的题:

每个人都有一个能力值ai,而如果两个人之间的能力值差距过大,那么两个人就无法交流,然而每个人的沟通能力也是不同的,具体来讲,每个人只能接受能力值在[li,ri]内的人做他的队友,想让队伍里的人数尽可能的多,请你输出这个最多的人数。

我们注意到,假设在队伍人数最多时,所有的人能接受的区间为[L,R],那么对每个人来说li <= L <= a,a <= R <= ri,所以我们可以把每个人看成一个矩形,矩形水平方向边为[li,ai],竖直方向的边为[ai,ri],问题就转化为找到一个点使得覆盖这个点矩形数目最多,那就直接扫描线,每次扫到一条边就区间+1或-1,维护区间最大值,就可以了。复杂度O(nlogn)。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<iostream>
#include<ctime>
#define B printf("Break\n");
#define A(x) cout << #x << " " << (x) << endl;
#define ll long long
using namespace std;
int read()
{
    int x = 0,f = 1;
    char c = getchar();
    while(c < '0' || c > '9') 
    {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
    {
        x = (x << 3) + (x << 1) + c - '0';
        c = getchar();
    }
    return f * x;
}
#define N 300010
int ans;
struct Rec
{
    int l,r,h;
    int op;
    bool operator < (const Rec &x) const
    {
        if(h == x.h) return op > x.op;
        return h < x.h;
    }
}rec[N << 1];
#define ls x << 1,l,mid
#define rs (x << 1 | 1),mid + 1,r
#define mid ((l + r) >> 1)
int tree[N << 2],lazy[N << 2];
void add(int x,int l,int r,int k)
{
    tree[x] += k;
    lazy[x] += k;
    return;
}
void push_down(int x,int l,int r)
{
    if(lazy[x] == 0) return;
    add(ls,lazy[x]);
    add(rs,lazy[x]);
    lazy[x] = 0;
    return;
}
void modify(int x,int l,int r,int p,int q,int k)
{
    if(p <= l && r <= q)
    {
        add(x,l,r,k);
        return;
    }
    push_down(x,l,r);
    if(p <= mid) modify(ls,p,q,k);
    if(q > mid) modify(rs,p,q,k);
    tree[x] = max(tree[x << 1],tree[x << 1 | 1]);
    return;
}
int cnt;
void Add(int l,int a,int r)
{
    rec[++cnt].l = l;
    rec[cnt].r = a;
    rec[cnt].h = a;
    rec[cnt].op = 1;
    rec[++cnt].l = l;
    rec[cnt].r = a;
    rec[cnt].h = r;
    rec[cnt].op = -1;
    return;
}
int main()
{
    int n = read();
    for(int i = 1;i <= n;i++) 
    {
        int l = read(),a = read(),r = read();
        Add(l,a,r);
    }
    sort(rec + 1,rec + 1 + cnt);
    for(int i = 1;i <= cnt;i++)
    {
        int l = rec[i].l,r = rec[i].r;
        modify(1,1,300000,l,r,rec[i].op);
        ans = max(ans,tree[1]);
    }
    printf("%d\n",ans);
}

猜你喜欢

转载自www.cnblogs.com/lijilai-oi/p/11103766.html