Picture 线段树 扫描线 Usaco

传送门
很裸的扫描线
0. 写在前面 对于 这类题 ,他们常有十分显著的特征基本上就是几个简单图形进行覆盖然 后统计周长或者面积,有的会利用“扫描”特点一些奇怪东西然而这也往存在

扫描线求面积
要直接统计一个图形的面积显然暴力求解复杂度实在是太 高,这时我们需要对其进行优化我们这时假设有一条扫描线,从下向上进行如图显然整个形的面积可以用 分割成的几个矩形面积加和来求解,而中间高度可以直接利用 Y2 -Y1 算出,那么我们其实只需要维护有多少条线段被覆盖即可
这里写图片描述

利用线段树的区间修改加以维护

扫描线求周长
其实这就和之前的东西一样了,只是需要横竖扫两遍。

值得注意的是,因为一条入边必然对应着一条出边,因而不需要lazy只需要维护一个覆盖数即可
其次 入边要排在出边得前面,才能保证前面的优化正确

下面是代码,我写的比较麻烦,是求周长的,稍微改一下就变成了求面积的,本类题其实常伴随着离散化等操作

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define N 40000+5
#define M 80000+5
using namespace std;
int ans,n;
struct data
{
    int l,r,c,m;
    int lazy;
    data () {}
}tr[M];
inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
struct ROW
{
    int y1,y2;
    int x;
    int in;
    bool operator < (const ROW &z)const
    {
        return x^z.x?x<z.x:in<z.in;
    }
    void output(int i)
    {
        printf("ROW[%d] x=%d %d %d\n",i,x-40,y1-40,y2 -40);
    }
}r[N];
struct LINE
{
    int y;
    int x1,x2;
    int in;
    bool operator < (const LINE &z)const
    {
        return y^z.y?y<z.y:in<z.in;
    }
    void output(int i)
    {
        printf("ROW[%d] y=%d %d %d\n",i,y-40,x1-40,x2-40 );
    }
}l[N];
void build(int k,int l,int r)
{
    tr[k].l=l,tr[k].r=r;
    if(l==r)
    {   tr[k].lazy=tr[k].m=tr[k].c=0;return;}
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    tr[k].lazy=tr[k].m=tr[k].c=0;
}
int CCC = 0;
void updata(int k)
{
    if(tr[k].l==tr[k].r)
    {
        if(tr[k].c)
            tr[k].m=1;
        else 
            tr[k].m=0;
        return ;
    }
    if(tr[k].c)
        tr[k].m=tr[k].r-tr[k].l+1;
    else 
        tr[k].m=tr[k<<1].m+tr[k<<1|1].m;
    //if(!tr[k].c)
    //  tr[k].c=min(tr[k<<1].c,tr[k<<1|1].c);
    return ;
}

void down(int k)
{
    int tmp=tr[k].lazy;
    tr[k<<1].lazy+=tmp;
    tr[k<<1].c+=tmp;
    updata(k<<1);

    tr[k<<1|1].lazy+=tmp;
    tr[k<<1|1].c+=tmp;
    updata(k<<1|1);

    tr[k].lazy=0;
    return;
}
void change(int k,int l,int r,int x)
{
    if(l==tr[k].l && r==tr[k].r)
    {
        //tr[k].lazy+=x;
        tr[k].c+=x;
        updata(k);
        if(CCC <= 3)
        {
            printf("k=%d  lazy=%d  c=%d  m=%d  tl=%d  tr=%d   l=%d  r=%d\n", k, tr[k].lazy, tr[k].c,
            tr[k].m, tr[k].l-40, tr[k].r-40, l-40, r-40);
        }
        return ;
    }
    //if(tr[k].lazy)
    //  down(k);
    int mid=(tr[k].l+tr[k].r)>>1;
    if(r<=mid)
        change(k<<1,l,r,x);
    else if(l>mid)
        change(k<<1|1,l,r,x);
    else 
    {
        change(k<<1,l,mid,x);
        change(k<<1|1,mid+1,r,x);
    }
    updata(k);
    if(CCC <= 3)
    {
            printf("2 k=%d  lazy=%d  c=%d  m=%d  tl=%d  tr=%d   l=%d  r=%d\n", k, tr[k].lazy, tr[k].c,
            tr[k].m, tr[k].l-40, tr[k].r-40, l-40, r-40);
    }
}
/*int ask(int k,int l,int r)
{
    if(l==tr[k].l && r==tr[k].r)
        return tr[k].m;
    if(tr[k].lazy)
        down(k);
    int mid=(tr[k].r+tr[k].l)>>1;
    if(r<=mid)
        return ask(k<<1,l,r);
    else if(l>mid)
        return ask(k<<1|1,l,r);
    return ask(k<<1,l,mid)+ask(k<<1,mid+1,r);
}*/
void add(int x1,int y1,int x2,int y2)
{
    static int cnt=0;
    r[++cnt].x=x1,r[cnt].y1=y1,r[cnt].y2=y2;
    r[cnt].in=0;
    l[cnt].y=y1,l[cnt].x1=x1,l[cnt].x2=x2;
    l[cnt].in=0;
    r[++cnt].x=x2,r[cnt].y1=y1,r[cnt].y2=y2;
    r[cnt].in=1;
    l[cnt].y=y2,l[cnt].x1=x1,l[cnt].x2=x2;
    l[cnt].in=1;
    return ;
}
void out()
{
    for(int i=1;i<=200;++i)
    {
        printf("%d %d %d %d\n",tr[i].l,tr[i].r,tr[i].c,tr[i].m);
    }
    return ;
}
int main()
{   
    //freopen("seg.in", "r", stdin);
    //freopen("tt.out", "w", stdout);
    cin>>n;
    for(int i=1,x1,x2,y1,y2;i<=n;++i)
    {
        x1=read()+10001,y1=read()+10001,x2=read()+10001,y2=read()+10001;
        add(x1,y1,x2,y2);
    }
    sort(l+1,l+2*n+1);
    sort(r+1,r+2*n+1);
    build(1,1,20002);
    int tmp=0;
    for(int i=1;i<=2*n;++i)
    {
        CCC=4;
        if(!l[i].in)
            change(1,l[i].x1,l[i].x2-1,1);
        else 
            change(1,l[i].x1,l[i].x2-1,-1);
        int ss=tr[1].m;
        //printf("pre=%d  now=%d  +=%d\n", tmp, ss, abs(ss-tmp) );
        ans+=abs(ss-tmp);
        tmp=ss;
        //out();
    }
    //printf("%d\n",ans );
    build(1,1,20002);
    tmp=0;
    for(int i=1;i<=2*n;++i)
    {
        if(!r[i].in)
            change(1,r[i].y1,r[i].y2-1,1);
        else
            change(1,r[i].y1,r[i].y2-1,-1);
        int ss=tr[1].m;
        //printf("pre=%d  now=%d  +=%d\n", tmp, ss, abs(ss-tmp) );
        ans+=abs(ss-tmp);
        tmp=ss;
    }
    cout<<ans;
}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值