POJ1436 Horizontally Visible Segments 线段树区间更新--区间覆盖

题目链接:http://poj.org/problem?id=1436


题目大意:有n条平行于y轴的线段,定义两线段可见,当且仅当这两条线段可以被一条水平线链接,且该水平线段不经过其他垂线段;定义三角形线段为两两可见的三条垂线段。现在给出n条线段,问你一共有多少组三角形线段。


分析:区间覆盖问题,POJ2777的升级版。两条线段a和b可见,就是说在存在某一区间[l,r],使得在此区间上b直接覆盖a。实现上,对于每一段区间,我们只纪录覆盖此区间的最后一条线段,设为color[rt],然后对于下一条覆盖此区间的线段id,我们就有了color[rt]和id是可见的,因为他们之间没有其他的线段了。

      我们可以先把n条线段按x坐标排序,然后从右至左遍历每一条线段,对于第i条线段,我们先判断在i的左边,有没有和它可见的线段,如果有(设为线段j),标记vis(i,j)=true,意为线段i和j可见;然后把这条线段插入到线段树中,更新他的y坐标所覆盖区间的color的值,这样,我们在遍历完这n条线段之后,他们之间可见的关系就全部标记出来, 然后我们暴力找一下其中的三角形线段的组数就行了。


实现代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int M=8010;
int color[M<<3]; //color[i]=j表示第i条线段最后被第j条线段覆盖
bool vis[M][M]; //vis[i][j]标记线段i和j是可见的
struct EDGE
{
    int y1,y2,x,id;
    EDGE(){}
    EDGE(int a,int b,int c,int d):y1(a),y2(b),x(c),id(d){}
    bool operator <(const EDGE &a) const
    {
        return x<a.x;
    }
}edge[M];
void pushdown(int rt)
{
    if(color[rt])
    {
        color[rt<<1]=color[rt<<1|1]=color[rt];
        color[rt]=0;
    }
}
void update(int rt,int l,int r,int id,int il,int ir)
{
    if(l>=il&&r<=ir)
    {
        color[rt]=id;
        return ;
    }
    pushdown(rt);
    int m=(l+r)>>1;
    if(il<=m) update(rt<<1,l,m,id,il,ir);
    if(ir>m) update(rt<<1|1,m+1,r,id,il,ir);
}
void query(int rt,int l,int r,int id,int il,int ir)
{
    if(color[rt])
    {
        vis[id][ color[rt] ]=true;
        return ;
    }
    if(l==r) return ;
    int m=(l+r)>>1;
    if(il<=m) query(rt<<1,l,m,id,il,ir);
    if(ir>m) query(rt<<1|1,m+1,r,id,il,ir);
}
int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int y1,y2,x;
            scanf("%d%d%d",&y1,&y2,&x);
            edge[i]=EDGE(y1*2,y2*2,x,i);
        }
        sort(edge+1,edge+1+n);
        memset(color,0,sizeof(color));
        memset(vis,false,sizeof(vis));
        for(int i=1;i<=n;i++)
        {
            query(1,0,M*2,edge[i].id,edge[i].y1,edge[i].y2);
            update(1,0,M*2,edge[i].id,edge[i].y1,edge[i].y2);
        }
        int ans=0;
        for(int i=1;i<=n;i++) //暴力找三角形线段的组数
          for(int j=1;j<=n;j++)
            if(vis[i][j])
              for(int k=1;k<=n;k++)
                if(vis[i][k]&&vis[j][k]) ans++;
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值