扫描线+线段树学习笔记
如果仔细观察扫描仪工作就会发现,扫描仪扫描时是一条线从头到尾扫一遍成像。这个算法形象化表示也是如此。
首先是扫描线板子题:矩形面积并。
题意:在平面直角坐标系中,给出若干个矩形,求所有矩形的面积并。
太长不看版:对于所有矩形的端点按照纵坐标排序,然后依次扫描矩形每加入一条线段覆盖,线段树查询所有区间中覆盖的长度即可。
详细来说,就是我们对每个矩形进行抽象化处理,把矩形的水平两条边单独拿出来,记录这条边的左右端点和高度。并且给这条边赋上一个特殊的值,为+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); }