扫描线

求矩形面积并

假设从左向右扫描,定义左侧的边是入边,右侧的边是出边。
用线段树存储线段,对于每一个节点记录其作为“整体最终划分节点”被统计了多少次。
(显然入边区间加一,出边区间减一,定义类似标记永久化无需 pushdown)。
update 的时候考虑如果当前区间已经被整体覆盖过就直接计算,否则使用子节点合并。

统计答案用横坐标之差乘上当前线段长进行累加即可。
建议相同横坐标合并处理。

#include<bits/stdc++.h>
using namespace std;
const int N=200009;
typedef long long ll;
struct line{int d,u,x,tag;}l[N];
bool cmp(line a,line b){return a.x<b.x;}
vector<int> vec;
class Segment{
    public:
        void modify(int i,int l,int r,int L,int R,int x){
            if(l==L&&r==R){tag[i]+=x,update(i,l,r); return;}
            int mid=(l+r)>>1;
            if(L>mid) modify(i<<1|1,mid+1,r,L,R,x);
            else if(R<=mid) modify(i<<1,l,mid,L,R,x);
            else modify(i<<1,l,mid,L,mid,x),modify(i<<1|1,mid+1,r,mid+1,R,x);
            update(i,l,r);
        }
        int rt(){return k[1];}
    private:
        int tag[N<<2]={},k[N<<2]={};
        void update(int i,int l,int r){
            if(tag[i]) k[i]=vec[r]-vec[l-1];
            else if(l!=r) k[i]=k[i<<1]+k[i<<1|1];
            else k[i]=0;
        }
}t;
int main(){
    int n,len; long long ans=0; scanf("%d",&n);
    for(int i=0,a,b,c,d;i<n;i++){
        scanf("%d%d%d%d",&a,&b,&c,&d);
        l[i<<1]={b,d,a,1},l[i<<1|1]={b,d,c,-1};
        vec.push_back(b),vec.push_back(d);
    }
    sort(vec.begin(),vec.end());
    len=unique(vec.begin(),vec.end())-vec.begin();
    for(int i=0;i<n+n;i++){
        l[i].u=lower_bound(vec.begin(),vec.begin()+len,l[i].u)-vec.begin();
        l[i].d=lower_bound(vec.begin(),vec.begin()+len,l[i].d)-vec.begin();
    }
    --len,sort(l,l+n+n,cmp);
    int it=0;
    for(;l[it+1].x==l[it].x;++it) t.modify(1,1,len,l[it].d+1,l[it].u,1);
    t.modify(1,1,len,l[it].d+1,l[it].u,1),it++;
    for(;it<n+n;){
    	ans+=1ll*t.rt()*(l[it].x-l[it-1].x);
        do t.modify(1,1,len,l[it].d+1,l[it].u,l[it].tag),++it; while(l[it].x==l[it-1].x&&it<n+n);
    }
    printf("%lld\n",ans);
    return 0;
}
求矩形周长并

主体同上,考虑统计答案每次用当前线段长减去上次的线段长即可,横竖各一次即可得到答案。
考虑极端边界情况需要先加后减统计差值,排序逻辑改一下即可。

posted @ 2025-09-22 21:43  2025ing  阅读(9)  评论(0)    收藏  举报