/*
题目描述:给出n(0 < n <= 1e5)条水平或竖直的线段,且同一方向上的线段不存在重合,问这些线段一共有多少交点
方法:将水平线段存在vy中,竖直线段存在vx中,将vx、vy按照先左后右、先下后上的顺序排序,然后对y方向的坐标
进行离散化。然后从左向右依次取出一条竖直的线段,计算每一条竖直线段与水平线段交点的个数,将他们相加就
是答案,具体方法如下:
用一个树状数组维护每个y高度存在的线段的个数,这样可以在logn的时间内统计出从y1到y2高度中的水平线段
有多少。假设当前拿出来的竖直线段为(x0 , r , x0 , s),首先找到所有满足x1 <= x0 <= x2的水平线段(x1 , y0 , x2 , y0),
然后将y0在树状数组中加一,然后树状数组查询一次得交点个数;然后再拿出下一条竖直线段(x0' , k , x0' , t),先将刚才
在树状数组中做过标记的水平线段中不满足x1 <= x0' <= x2的线段对应的y0位置减一,然后再将新的满足条件的水平
线段加进来,然后求这条竖直线段与水平线段的交点个数;依此类推求第三条第四条第五条等等的竖直线段交点数
判断x1 <= x0 <= x2的方法见代码
*/
#pragma warning(disable:4786)
#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<string>
#include<sstream>
#define LL long long
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define lson l,m,x<<1
#define rson m+1,r,x<<1|1
using namespace std;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double PI = acos(-1.0);
const double eps=1e-8;
struct node
{
int x1 , y1 , x2 , y2;
bool operator < (const node & rhs)const{
return x2 > rhs.x2;
}
};
bool cmp(const node a , const node b)
{
if(a.x1 < b.x1)
return true;
else if(a.x1 == b.x1)
return a.y1 < b.y1;
else
return false;
}
vector< node >vx;
vector< node >vy;
vector< int >v;
map<int , int>mp;
int n , num1 , num2 , cnt1 , cnt2;
int c[300000];
priority_queue<node>Q;
int lowbit(int x)
{
return x & (-x);
}
void modify(int x , int val)
{
while(x <= cnt2){
c[x] += val;
x += lowbit(x);
}
}
LL sum(int x)
{
LL res = 0;
while(x > 0){
res += c[x];
x -= lowbit(x);
}
return res;
}
void init()
{
mem(c , 0);
v.clear();
vx.clear(); vy.clear();
mp.clear();
while(!Q.empty())
Q.pop();
}
void read()
{
int x1 , y1 , x2 , y2;
node st;
scanf("%d",&n);
for(int i = 0 ; i<n ;i++){
scanf("%d %d %d %d",&x1 , &y1 , &x2 , &y2);
if(x1 == x2){
st.x1 = x1 ; st.x2 = x2;
if(y1 > y2){
swap(y1 , y2);
}
st.y1 = y1; st.y2 = y2;
vy.push_back(st);
v.push_back(y1); v.push_back(y2);
}
else{
st.y1 = y1; st.y2 = y2;
if(x1 > x2){
swap(x1 , x2);
}
st.x1 = x1; st.x2 = x2;
vx.push_back(st);
v.push_back(y1);
}
}
}
void discretization()
{
sort(v.begin() , v.end());
cnt2 = unique(v.begin() , v.end() ) - v.begin();
for(int i = 0 ; i<cnt2 ; i++){
mp[v[i]] = i + 1;
}
for(int i = 0 ; i < vx.size() ; i++){
vx[i].y1 = mp[vx[i].y1];
vx[i].y2 = mp[vx[i].y2];
}
for(int i = 0 ; i < vy.size() ; i++){
vy[i].y1 = mp[vy[i].y1];
vy[i].y2 = mp[vy[i].y2];
}
sort(vx.begin() , vx.end() , cmp);
sort(vy.begin() , vy.end() , cmp);
}
int main()
{
int T ;
scanf("%d", &T);
while(T--){
init();
read();
discretization();
node st , cur;
int i , j = 0;
num1 = vy.size();
num2 = vx.size();
LL ans = 0;
for(i = 0 ; i < num1 ; i++){
st = vy[i];
for(;j < num2; j++){
if(vx[j].x1 <= st.x1){ //将左端点小于等于当前竖直线段x坐标的线段入队并对其y坐标进行标记
Q.push(vx[j]);
modify(vx[j].y1 , 1);
}
else break;
}
while(!Q.empty()){ //将队列中右端点小于当前竖直线段x坐标的线段出队并取消对其y坐标标记
cur = Q.top();
if(cur.x2 >= st.x1) break;
else{
Q.pop();
modify(cur.y1 , -1);
}
}
ans += sum(st.y2) - sum(st.y1 - 1);
}
printf("%lld\n",ans);
}
return 0;
}
hdu5862 Counting Intersections
最新推荐文章于 2017-08-18 21:08:07 发布