2020NYIST个人积分赛第二场 (线段树扫描线扫求周长)

题意:

给n个矩形坐标,可能重合,求整体周长

思路:

扫描线扫两遍,从左到右扫一遍,从上到下扫一遍,但是和扫面积略有不同,我们的tr[1].sum的值需要和上一条边进行比较,然后再添加到ans中,我们利用线段树求出tr[1].sum便是当前扫到的整体长度,累加求和即可

注意:
1:这题多组输入,一开始没注意,wa了两发
2:注意清空线段树中的标记和区间长度,遍历一下节点即可清空

参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
#define inf 1<<28
const ll base=13331;
const ll maxn=1e5+5;
#define lson k<<1
#define rson k<<1|1
#define mid ((l+r)>>1)
using namespace std;
struct line
{
    ll x,y,h,lazy;
} p[maxn<<1];
struct node
{
    ll sum,num,len;
    bool lflag,rflag;
} tr[maxn<<3];
ll n,mx=-inf,mn=inf,cnt,ans,las;
void add(ll l,ll r,ll h,ll f)
{
    p[++cnt].x=l;
    p[cnt].y=r;
    p[cnt].h=h;
    p[cnt].lazy=f;
}
bool cmp(line a,line b)
{
    return a.h<b.h||a.h==b.h&&a.lazy>b.lazy;
}
void pushup(ll k,ll l,ll r)
{
    if(tr[k].sum)
    {
        tr[k].num=1;
        tr[k].len=r-l+1;
        tr[k].lflag=tr[k].rflag=1;
    }
    else if(l==r)
    {
        tr[k].len=0;
        tr[k].num=0;
        tr[k].lflag=tr[k].rflag=0;
    }
    else
    {
        tr[k].len=tr[lson].len+tr[rson].len;
        tr[k].num=tr[lson].num+tr[rson].num;
        if(tr[lson].rflag&&tr[rson].lflag)tr[k].num--;
        tr[k].lflag=tr[lson].lflag;
        tr[k].rflag=tr[rson].rflag;
    }
}
void build(int k,int l,int r)
{
    tr[k].lflag=tr[k].rflag=false;
    tr[k].sum=0;
    if(l==r)
        return;
    build(lson,l,mid);
    build(rson,mid+1,r);
}
void modify(ll k,ll l,ll r,ll L,ll R,ll value)
{
    if(l>=L&&r<=R)
    {
        tr[k].sum+=value;
        pushup(k,l,r);
        return;
    }
    if(L<=mid)modify(lson,l,mid,L,R,value);
    if(R>mid)modify(rson,mid+1,r,L,R,value);
    pushup(k,l,r);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n&&n)
    {
        mx=-inf,mn=inf,cnt=0,ans=0,las=0;
        build(1,1,n);
        for(ll i=1; i<=n; i++)
        {
            ll x1,y1,x2,y2;
            cin>>x1>>y1>>x2>>y2;
            mx=max(mx,max(x1,x2));
            mn=min(mn,min(x1,x2));
            add(x1,x2,y1,1);
            add(x1,x2,y2,-1);
        }
        if(mn<=0)
        {
            for(ll i=1; i<=cnt; i++)
            {
                p[i].x+=-mn+1;
                p[i].y+=-mn+1;
            }
            mx-=mn;
        }
        sort(p+1,p+cnt+1,cmp);
        for(ll i=1; i<=cnt; i++)
        {
            modify(1,1,mx,p[i].x,p[i].y-1,p[i].lazy);
            while(p[i].h==p[i+1].h&&p[i].lazy==p[i+1].lazy)
            {
                i++;
                modify(1,1,mx,p[i].x,p[i].y-1,p[i].lazy);
            }
            ans+=abs(tr[1].len-las);
            las=tr[1].len;
            ans+=tr[1].num*2*(p[i+1].h-p[i].h);
        }
        cout<<ans<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值