poj 1436 区间线段树 离散化

/*
    线段树  扩点 扩线  区间覆盖  好题  


    题意:给出n条平行于Y轴的线段(y', y'', x),然后3条一组,问有多少组可见线段组。“可见”的定义为,两条线段能由一条水平先连接但不交于其它的线段。“可见线段组”的定义为该组内的3条线段两两可见。




    心得:之前看poj_3225,死活看不懂,跳过了。做这道题,它们的离散化方法是一样的:线段 = 一个闭区间 + 两个端点。而线段树是只存点的,为了解决这个问题我们可以把这个闭区间也化成一个点,这样就可以存入线段树里了。而线段 * 2这一方法正好可以实现这一点。如下图,
        区间[0,2]离散化后就变成这么些点:
        真实的线段:          [0], (0,1), [1], (1,2), [2] ...
        化为线段树里对应的点: 0     1     2     3     4  ...
        


        当然也可以用poj_2528这样的方法来做,不过就是要处理的东西多些,麻烦些。poj_2528的解法是应对区间过大时防止MLE/TLE的解法,跟这道题不一样,这道题的重点是同化区间和点,poj_2528的重点是缩短区间的长度。
        *** 偶数点代表点,奇数代表线段,遇到有线段类的题目(用线段树做)经常要考虑乘以2,表示浮点的线段 ***


    思路:
    
    下面摘抄自大牛们的blog:
    "能明显的感觉到是区间覆盖问题了。但是有一个细节问题,就是中间的水平线不一定经过整点,所以即使这个区间的所有点都被覆盖,也不能说其就不能穿过一条线,于是,可以将所有线段的长度扩大至2倍,这样就解决了这个问题。"
    "从左到右,一次对每条线段,先进行查询,看左边能看见多少条线段,然后进行覆盖,因为很明显,如果一条线段能看见另一条线段,那么这个关系必然是相互的,所以对每条线段,只需要往左看就行了"
    "注意:如 样例 中 ,0 2 2,3 4 2这两条线段,可以看到 2 3之间是没有被覆盖的,但是在线段树中我们看不到这条线段,因为 变成 浮点数了,不能处理,那么我们可以将 坐标 x2,这样就变成 4 6,中间就多出一个点 5 了,就可以判断了。。
    偶数点代表点,奇数代表线段,遇到有线段类的题目(用线段树做)经常要考虑乘以2,表示浮点的线段。。。poj  3225这题类似"
    "注意:由于线段包括端点,如果这条直线y=c刚好穿过某条线段的端点,则情况会变得有些麻烦.可以采用这种方法来做:将上下纵坐标乘2,横坐标不变,改变原来[y,y+1)的节点存储方式,变为[y,y]式,这样,就可以简单地处理端点问题,并且它对于所有情况都有很好的效果.自己画个图就明白了."

*/


#include <algorithm>
#include <vector>
#include <stdio.h>
#include <string.h>
using namespace std;
#define     MAXN        8005
#define     MAX_HIGH        8000 * 2
#define     debug       printf("!\n")
#define     MID(x,y)    (((x)+(y))>>1)
#define     L(x)        ((x)<<1)
#define     R(x)        (((x)<<1)|1)

struct Line {
    int x, y1, y2;
} line[MAXN];
struct Seg_tree_Node {
    int l, r, label;
} f[MAX_HIGH * 4];
bool path[MAXN][MAXN];
vector<int> vis[MAXN];
int n;

void init()
{
    memset(path, false, sizeof(path));
    for(int i = 1; i <= n; i++) vis[i].clear();
}
void build(int u, int l, int r)
{
    f[u].l = l, f[u].r = r, f[u].label = 0;
    if(l == r) return ; 
    int mid = MID(l, r);
    build(L(u), l, mid);
    build(R(u), mid+1, r);
}
inline bool cmp(const Line & a, const Line & b)
{
    return a.x < b.x;
}
void Query(int u, int l, int r, int now)
{
    if(f[u].label == 0) return;
    else if(f[u].label != -1) {
        if(path[now][f[u].label]) return ;
        path[now][f[u].label] = true;
        vis[now].push_back(f[u].label);
        return ;
    }
    int mid = MID(f[u].l, f[u].r);
    if(r <= mid) Query(L(u), l, r, now);
    else if(mid < l) Query(R(u), l, r, now);
    else {
        Query(L(u), l, mid, now);
        Query(R(u), mid+1, r, now);
    }
}
void push_down(int u)
{
    f[L(u)].label = f[R(u)].label = f[u].label;
    f[u].label = -1;
}
void Update(int u, int l, int r, int now)
{
    if(l == f[u].l && f[u].r == r) {
        f[u].label = now;
        return;
    }
    if(f[u].label > 0) push_down(u);
    f[u].label = -1;
    int mid = MID(f[u].l, f[u].r);
    if(r <= mid) Update(L(u), l, r, now);
    else if(mid < l) Update(R(u), l, r, now);
    else {
        Update(L(u), l, mid, now);
        Update(R(u), mid+1, r, now);
    }
}
int calc()
{
    int ans = 0;
    for(int x = 1; x <= n; x++)
        for(int Size1 = vis[x].size(), i = 0; i < Size1; i++) {
            int y = vis[x][i];
            for(int Size2 = vis[y].size(), j = 0; j < Size2; j++) {
                int z = vis[y][j];
                if(path[x][z]) ans++;
            }
        }
//    for(int i = 1; i <= n; i++) {
//        printf("# %d : ", i);
//        for(int j = 0; j < vis[i].size(); j++) printf("%d  ", vis[i][j]);
//        printf("\n");
//    }
    return ans;   
}
int main()
{
    int cases;
    scanf("%d", &cases);
    while(cases--) {
        scanf("%d", &n);
        init();
        build(1, 0, MAX_HIGH);
        for(int i = 1; i <= n; i++) {
            scanf("%d%d%d", &line[i].y1, &line[i].y2, &line[i].x);
            line[i].y1 <<= 1;
            line[i].y2 <<= 1;
        }
        sort(line+1, line+n+1, cmp);
        for(int i = 1; i <= n; i++) {
            Query(1, line[i].y1, line[i].y2, i);
            Update(1, line[i].y1, line[i].y2, i);
        }
        printf("%d\n", calc());
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值