hdu5862Counting Intersections(树状数组)

题目链接:

http://acm.split.hdu.edu.cn/showproblem.php?pid=5862

题目大意:

给一些与坐标轴平行的线段,问有多少个交点。题目保证线段不会有重合的端点。

范围:

n<=100000。

思路:

根据题目的要求,我们可以知道交点一定是横线和竖线产生的。我们可以假设有一条扫描线从左往右扫过去。所以我们对于横线来说,遇到一个横线的左端点,就将他的y进行++操作,遇到右端点,就进行y--操作。如果遇到竖线,就对竖线[y1,y2]区间内进行统计。这两个操作可以利用树状数组维护完成。

为了保证上述操作的正确性,我们需要将坐标按x轴从小到大进行排序。注意当竖线和横线端点在同一个x的位置时,要保证左端点先加上去,然后统计,最后再将右端点减去。

这里我们可以利用一个flag标记,将左端点置为1,竖线置为0,右端点置为-1。

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll __int64
using namespace std;
struct node{
    int x,y,idx,flag,ff;
}p[200005];
int n,m,a[200005];
ll  d[200005];
pair<int,int>pp[200005];
bool cmp(node a,node b){
    if(a.x==b.x){
        return a.flag>b.flag;
    }
    return a.x<b.x;
}
int lowbit(int x){
    return x&(-x);
}
void update(int x,int z,int n){
    while(x<=n){
        d[x]+=z;
        x+=lowbit(x);
    }
}
ll getsum(int x){
    ll ans=0;
    while(x){
        ans+=d[x];
        x-=lowbit(x);
    }
    return ans;
}
int main()
{
    int i,j,x1,y1,x2,y2;
    int T;
    scanf("%d",&T);
    while(T--){
        memset(a,0,sizeof(a));
        memset(d,0,sizeof(d));
        scanf("%d",&n);
        int tt=0;
        for(i=1;i<=n;i++){
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            if(x1>x2){
                int tmp=x1;
                x1=x2;
                x2=tmp;
                tmp=y1;
                y1=y2;
                y2=tmp;
            }
            if(y1>y2){
                int tmp=x1;
                x1=x2;
                x2=tmp;
                tmp=y1;
                y1=y2;
                y2=tmp;
            }
                p[(i-1)*2+1].x=x1;
                p[(i-1)*2+1].y=y1;
                p[(i-1)*2+2].x=x2;
                p[(i-1)*2+2].y=y2;
                p[(i-1)*2+1].ff=p[(i-1)*2+1].ff=0;
                if(x1==x2)
                {
                     p[(i-1)*2+1].flag=p[(i-1)*2+2].flag=0;
                     p[(i-1)*2+1].ff=1;
                     p[(i-1)*2+2].ff=0;
                }

                else if(y1==y2)
                {

                    p[(i-1)*2+1].flag=1;
                    p[(i-1)*2+2].flag=-1;
                }
            a[++tt]=p[(i-1)*2+1].y;
            a[++tt]=p[(i-1)*2+2].y;
        }
        sort(a+1,a+1+tt);
        int xx=0;
        int siz=unique(a+1,a+1+tt)-a-1;
        for(i=1;i<=n;i++){
            p[2*i-1].y=lower_bound(a+1,a+1+siz,p[2*i-1].y)-a;
            p[2*i].y=lower_bound(a+1,a+1+siz,p[2*i].y)-a;
            if(p[2*i-1].x==p[2*i].x){
                    pp[++xx]=make_pair(p[2*i-1].y,p[2*i].y);
                    p[2*i-1].ff=xx;
            }
        }
        sort(p+1,p+1+2*n,cmp);
        ll ans=0;
        for(i=1;i<=2*n;i++){
            if(p[i].flag!=0){
                update(p[i].y,p[i].flag,2*n);
            }
            else {
                    if(p[i].ff){
                            int t=p[i].ff;
                         ans+=getsum(pp[t].second)-getsum(pp[t].first-1);
                    }

            }
        }
        printf("%I64d\n",ans);
    }
}
/*
2
4
0 0 4 0
4 0 4 2
4 -1 4 1
4 2 5 2
3
0 0 2 0
2 1 2 2
2 0 2 2
*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值