POJ 1436 Horizontally Visible Segments

题意:给定n条竖直的线段,求有多少个三元组,使得这三条线段两两可见(可见的定义为这两条线段可以连一条水平线段而不与任意其他线段相交)

题解:将线段按照x坐标排序,判断两个线段是否可见,就是将后一个线段向左投影能够在第一个线段上留下阴影。用线段树代表某个区间当前阴影代表谁,处理新线段即给那段区间赋值,并记录会覆盖哪些值就行。因为要考虑阴影投射在非整数点,且线段y坐标都是整数,所以线段树的大小要乘二,用2y代表原来的y坐标。知道了两两可见的情形,枚举一下就可以出答案了。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=8005;
struct Line
{
    int left,right,val;
}line[N*8];
int head[N],nc;
struct Edge
{
    int to,next;
}edge[N*1000];
bool g[N][N];
int lis[N];
struct Data
{
    int x,y1,y2;
    bool operator<(const Data &ne)const
    {
        return x<ne.x;
    }
}data[N];
void add(int a,int b)
{
    if(g[a][b])
        return;
    edge[nc].to=b;edge[nc].next=head[a];head[a]=nc++;
    g[a][b]=g[b][a]=true;
    //printf("->%d %d\n",a,b);
}
void build(int now,int left,int right)
{
    line[now].left=left;
    line[now].right=right;
    line[now].val=0;
    if(left==right)
        return;
    build(now*2,left,(left+right)>>1);
    build(now*2+1,(left+right)/2+1,right);
}
void updata(int now,int ll,int rr,int val)
{
    int left=line[now].left,right=line[now].right,mid=(left+right)/2;
    if(left==ll&&rr==right&&line[now].val!=-1)
    {
        if(line[now].val!=0)
            add(line[now].val,val);
        line[now].val=val;
    }
    else
    {
        if(line[now].val!=-1)
        {
            line[now*2].val=line[now*2+1].val=line[now].val;
            line[now].val=-1;
        }
        if(mid>=rr)
            updata(now*2,ll,rr,val);
        else if(mid<ll)
            updata(now*2+1,ll,rr,val);
        else
        {
            updata(now*2,ll,mid,val);
            updata(now*2+1,mid+1,rr,val);
        }
    }
}
int main()
{
    int T;
    for(scanf("%d",&T);T;T--)
    {
        int n,ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&data[i].y1,&data[i].y2,&data[i].x);
        sort(data+1,data+1+n);
        build(1,0,16000);
        memset(g,false,sizeof(g));
        memset(head,-1,sizeof(head));
        nc=0;
        for(int i=1;i<=n;i++)
            updata(1,data[i].y1*2,data[i].y2*2,i);
        for(int i=1,top;i<=n;i++)
        {
            top=0;
            for(int j=head[i];j!=-1;j=edge[j].next)
                lis[top++]=edge[j].to;
            for(int j=0;j<top;j++)
                for(int k=j+1;k<top;k++)
                if(g[lis[j]][lis[k]])ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值