2018-2019 ACM-ICPC Asia Seoul Regional Contest A Circuits Gym - 101987A (线段树+lazy标记)

本文介绍了一种使用线段树解决矩形覆盖问题的算法,通过坐标离散化减少数据范围,利用线段树存储区间最大值和lazy标记,实现高效查询和更新。文章详细解释了两种遍历纵坐标求解最大覆盖数的方法,并提供了具体代码实现。
摘要由CSDN通过智能技术生成

用线段树做这题至少有两种写法

     输入数据的离散,建树,求每个离散后的纵坐标穿过的矩形数都一样,不同的地方在遍历纵坐标求答案。

     线段树存的是lazy标记和区间内最大值

     1.输入矩形的横坐标没用,只要储存纵坐标。坐标范围达到10的7次方,故先离散化。把每个矩形表示成纵坐标对应的线段即可

     2.在线段树中把这些区间值加一(建树),注意要用lazy标记,顺便分别求出以每个点为起点、终点的线段是哪些(这里可以开两个比较大(200010)的vector数组 )。

     3.查询每个点的值并存入数组ans[]内,即离散后每个纵坐标(其实已经是线段的端点了)穿过的矩阵数。

     接下来要遍历所有线段的两个端点并找到最大的答案,此时有两种写法:

      一种是从前往后遍历每个点,先把以该点(i)为起点的线段从树中删去(即区间值减一),此时tree[1].max(线段树最大区间内的最大值)+ans[i]即为可能的答案,最后把以该点(i)为终点的线段加入树中(即区间值加一),本次操作结束。

      取每次循环得到的答案的最大值即为结果。这种写法保证遍历每个点的时候已经把该点穿过的矩阵删去,故tree[1].max可以表示其他点的最大穿过数。

       另外一种也是从前往后遍历每个点,不同的是只把以该点(i)为起点的线段从树中删去,查询查从i到最后一个点的最大值,查询结果+ans[i]为可能的答案,不需要把以该点(i)为终点的线段加入树中(即区间值加一)。

      同样取每次循环得到的答案的最大值即为结果。但是这种写法利用了从前往后遍历的性质,只考虑i和i后面的点即可,因为i和i前面的点已经在之前出现过了。

  本文给出第一种写法

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
int ux[100010],uy[100010],dis[200010],con=1,ans[200010];
vector<int>gl[200010],gr[200010];
struct node
{
    int m,f;
}tree[800010];
void pushdown(int now,int l,int r)
{
    tree[l].m+=tree[now].f;
    tree[r].m+=tree[now].f;
    tree[l].f+=tree[now].f;
    tree[r].f+=tree[now].f;
    tree[now].f=0;
}
void change(int l,int r,int now,int x,int y,int h)
{
    if(x<=l&&r<=y)
    {
        tree[now].f+=h;
        tree[now].m+=h;
        return;
    }
    int mid=(l+r)>>1;
    if(tree[now].f)
    pushdown(now,now<<1,now<<1|1);
    if(y<=mid)
        change(l,mid,now<<1,x,y,h);
    else if(x>mid)
        change(mid+1,r,now<<1|1,x,y,h);
    else
    {
        change(l,mid,now<<1,x,mid,h);
        change(mid+1,r,now<<1|1,mid+1,y,h);
    }
    tree[now].m=max(tree[now<<1].m,tree[now<<1|1].m);
}
int query(int l,int r,int now,int x)
{
    if(l==r)
    return tree[now].m;
    if(tree[now].f)
    pushdown(now,now<<1,now<<1|1);
    int mid=(l+r)>>1;
    if(x<=mid)
    return query(l,mid,now<<1,x);
    else
    return query(mid+1,r,now<<1|1,x);
}
int main()
{
   // freopen("1.txt","r",stdin);
    int n;
    cin>>n;
    for(int i=1;i<=n;++i)
    {
        int a;
        scanf("%d%d%d%d",&a,&uy[i],&a,&ux[i]);
        dis[con++]=uy[i];
        dis[con++]=ux[i];
    }
    sort(dis+1,dis+con);
    con=unique(dis+1,dis+con)-dis-1;
    for(int i=1;i<=n;++i)
    {
        ux[i]=lower_bound(dis+1,dis+con+1,ux[i])-dis;
        uy[i]=lower_bound(dis+1,dis+con+1,uy[i])-dis;
        gl[ux[i]].push_back(i);
        gr[uy[i]].push_back(i);
        change(1,con,1,ux[i],uy[i],1);
    }
    for(int i=1;i<=con;++i)
    {
    ans[i]=query(1,con,1,i);
    }
    int Max=0;
    for(int i=1;i<=con;++i)
    {
        for(int j=0;j<gl[i].size();++j)
        change(1,con,1,ux[gl[i][j]],uy[gl[i][j]],-1);
        Max=max(Max,ans[i]+tree[1].m);
        for(int j=0;j<gr[i].size();++j)
        change(1,con,1,ux[gr[i][j]],uy[gr[i][j]],1);
    }
    printf("%d\n",Max);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值