POJ 1436 Horizontally VisibleSegments(线段树:区间覆盖染色)

POJ 1436 Horizontally VisibleSegments(线段树:区间覆盖染色)

http://poj.org/problem?id=1436点击打开链接

题意:

    给出N(N <= 8000)条垂直线段,如果两条线段在水平方向上连一条线之后不和其他任何垂直线段相交,那么我们称这两条线段水平可见,如果三条垂直线段两两水平可见,则称其为一个三角,问着N条线段能组成多少三角。

分析:

首先如果我们可以求得任意两条垂直线段i和j之间是否可见mark[i][j],那么我们可以用暴力O(n^3)遍历的方式来得到所有三角个数。所以下面我们只要求出这个mark矩阵即可。本题就变成了一个线段树区间覆盖染色问题了。

1.    首先读入所有的线段,然后按x轴坐标从小到大排序,然后我们一次处理每条线段,用一个二维矩阵mark[i][j]保存第i条线段和第j条线段之间的关系.(如果相互可以看到,则mark为true,否则为false).

2.    下面的问题是如果求出任意两线段之间的可见关系呢?把线段按x坐标排序,如果第4条线段和第7条线段可见:含义是已经放好了第5条和第6条线段第7条线段依然能看到第4条.所以我们只需要求出第i条线段与前i-1条线段的可见关系即可.

3.    线段树维护color信息,color>0时,表示节点已经被某个线段覆盖,color=0表示节点没有被任何线段覆盖,color=-1,表示节点被多种情况覆盖(其子节点可能被多个线段分别覆盖,或者有些根本没覆盖).每加入一条新线段,我们先查询它能看到的color,然后更新节点覆盖情况.

4.    如果有4条线段区间依次(依x从小到大的次序)分别为[2,3][1,2],[3,4]和[1,4],那么[1,4]能看到的线段应该是3条,但是如果我们按照上面的做法[1,4]就只能看到两条线,因为开区间(2,3)未被考虑.所以为了考虑开区间,我们把所有的节点的y坐标加倍(由于老的区间是[0,8000],新的区间节点为0-16000,程序为了保险设为16000+2),即以前的区间[1,1]用[2,2]表示,[2,2]用[4,4]表示,所以新的3用来表示开区间(1,2).类似于POJ3225:

http://blog.csdn.net/u013480600/article/details/22284341

 

5.    build函数:初始所有color全为0,所以可以不要.

 

6.    PushUp函数:根据两个子节点的color确定

 

7.    PushDown函数:把color信息下放下去.原i节点的color信息可以不管,因为PushDown之后必然有个PushUp.(在update中用PushDown)

 

8.    query函数:如果待查询区间的color不为-1(为0或>0),那么直接处理不用去找子节点了,否则还需要继续找子节点.

 

9.    update函数:区间包含直接更新,否则先PushDown然后分别更新子节点.最后PushUp.

 

AC代码:1407ms,编译通过,一次AC

 

<span style="font-size:18px;">#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define lson i*2,l,m
#define rson i*2+1,m+1,r
using namespace std;
const int MAXN = 8000 * 2 + 2;
struct node
{
    int y1, y2, x;
    bool operator <(const node &b)const
    {
        return x < b.x;
    }
} nodes[MAXN / 2];

struct IntervalTree
{
    int color[MAXN * 4];
    bool mark[MAXN / 2][MAXN / 2];
    void init()
    {
        memset(color, 0, sizeof(color));
        memset(mark, 0, sizeof(mark));
    }
    void PushUp(int i)
    {
        int lc = i * 2, rc = i * 2 + 1;
        if(color[lc] == -1 || color[rc] == -1)
            color[i] = -1;
        else if(color[lc] == color[rc])
            color[i] = color[lc];
        else
            color[i] = -1;
    }
    void PushDown(int i)
    {
        if(color[i] > 0)
        {
            color[i * 2] = color[i * 2 + 1] = color[i];
        }
    }
    void query(int ql, int qr, int id, int i, int l, int r)
    {
        if(color[i] != -1)
        {
            mark[id][color[i]] = mark[color[i]][id] = true;
            return ;
        }
        //PushDown(i);//此句可以不要,因为color必然==-1
        int m = (l + r) / 2;
        if(ql <= m) query(ql, qr, id, lson);
        if(m < qr) query(ql, qr, id, rson);
    }
    void update(int ql, int qr, int id, int i, int l, int r)
    {
        if(ql <= l && r <= qr)
        {
            color[i] = id;
            return ;
        }
        //if(l == r)return ; //此句多余
        PushDown(i);
        int m = (l + r) / 2;
        if(ql <= m) update(ql, qr, id, lson);
        if(m < qr) update(ql, qr, id, rson);
        PushUp(i);
    }
};

IntervalTree T;

int main()
{
    int K;
    scanf("%d", &K);
    while(K--)
    {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%d%d", &nodes[i].y1, &nodes[i].y2, &nodes[i].x);
            nodes[i].y1 <<= 1;
            nodes[i].y2 <<= 1;
        }
        sort(nodes + 1, nodes + n + 1);
        T.init();
        for(int i = 1; i <= n; i++)
        {
            T.query(nodes[i].y1, nodes[i].y2, i, 1, 1, MAXN);
            T.update(nodes[i].y1, nodes[i].y2, i, 1, 1, MAXN);
        }
        int ans = 0;
        for(int i = 1; i <= n; i++)
            for(int j = i + 1; j <= n; j++)if(T.mark[i][j])
                    for(int k = j + 1; k <= n; k++)
                        if(T.mark[i][k] && T.mark[j][k])
                            ans++;
        printf("%d\n", ans);
    }
    return 0;
}
</span>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值