HDU 3465 【树状数组/几何问题转化逆序问题】
建议先做:POJ 3067
题意:给你N对点和区间L,R,每对点都会形成一条直线,问你这些直线的交点中横坐标大于L,且小于R的数量是多少。
思路:首先画一个满足条件的直线可以发现要想它们的交点在
(L,R),必然满足(y1-y2)*(y3-y4)<0,解释如图,那么只要求出来每条直线在L和R处的y1,y2,不就跟POJ 3067类似了吗?逆序数问题啊!!!但是这里是小数,树状数组怎么存呢?我们不妨设y1为横坐标,y2为纵坐标,对所有的点对于x从小到大排个序,然后给坐标id,不就转化成整数了吗?然后按Y从小到大排序,直接树状数组逆序更新求和。
坑点:斜率为+00怎么办,如果是(L,R)里面的,直接记录,肯定会跟每个不与它平行的直线有交点,对答案的贡献就是cnt*num。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int c[maxn];
struct node
{
double x,y;
int id;
} a[maxn];
bool cmp1(node a,node b)
{
if(a.x!=b.x)
return a.x<b.x;
return a.y<b.y;
}
bool cmp2(node a,node b)
{
if(a.y!=b.y)
return a.y>b.y;
return a.x>b.x;
}
int lowbit(int x)
{
return x&(-x);
}
void add(int x)
{
while(x<maxn)
{
c[x]++;
x+=lowbit(x);
}
}
int query(int x)
{
int ans=0;
while(x)
{
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
int main()
{
int n;
double l,r,x1,x2,y1,y2;
while(~scanf("%d",&n))
{
memset(c,0,sizeof c);
scanf("%lf%lf",&l,&r);
int cn=0,cnt=0;
for(int i=1; i<=n; i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
if(x1==x2)
{
if(x1>l&&x1<r) cnt++;
continue ;
}
a[++cn].x=y2+(y2-y1)/(x2-x1)*(l-x2);
a[cn].y=y2+(y2-y1)/(x2-x1)*(r-x2);
}
sort(a+1,a+cn+1,cmp1);
for(int i=1; i<=cn; i++)
a[i].id=i;
sort(a+1,a+cn+1,cmp2);
ll ans=cn*cnt;
for(int i=1; i<=cn; i++)
{
add(a[i].id);
ans+=query(a[i].id-1);
}
printf("%d\n",ans);
}
}