题目传送门
题意:
二维平面内n个不同位置的横纵坐标都为整数的点,假如存在多于一个点位于同一条直线,那么就画出这条直线。询问有多少对直线相交。
数据范围:n <= 1000 , -10000 <= 横纵坐标 <= 10000。
题解:
自定义:垂直于y轴的线叫横线。垂直于x轴的线叫竖线。其他直线叫斜线。
答案由4部分累加得来:
(1)所有的横线与所有的竖线两两相交。
(2)所有的斜线与所有的横线两两相交。
(3)所有的斜线与所有的竖线两两相交。
(4)所有不平行的斜线两两相交。
最后一部分是斜线之间的计算:首先建立一个map,判定同斜率且同截距是否出现过。注意斜率相同的直线不相交。
关于斜率和截距的存储方式,可以用double,也可以用最简分数,这两种我都试了试并且都可以过,其实我觉得由于精度的关系double不是很稳,但它确实是过了,并且比最简分数要快。
感受:
这道题题意是非常的简单,我就误以为做法很简单,第一反应还在想这题为什么会有2000分。结果写着写着发现确实是有点麻烦。我认为与其称它是计算几何的题目,不如称为模拟题。因为我计算几何一点不会,其实是通过分类讨论把它模拟出来的。
代码1(double存储斜线)1123 ms:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
typedef pair<int , int> pii ;
const int maxn = 1005 ;
const int maxm = 2e4 + 5 ;
set<int> row[maxm] , col[maxm] ;
int n ;
map<pair<pii , pii> , bool> vis ;
map<pii , int> id ;
ll b[maxn * maxn] ;
int cur = 0 ;
struct node
{
int x , y ;
bool operator< (const node &s) const
{
if(y != s.y) return y < s.y ;
else return x < s.x ;
}
} a[maxn] ;
void change(int &x , int &y)
{
if(x * y > 0)
x = abs(x) , y = abs(y) ;
else
x = -abs(x) , y = abs(y) ;
int d = __gcd(abs(x) , y) ;
x /= d , y /= d ;
}
bool ok(int i , int j)
{
int dx = a[j].x - a[i].x ;
int dy = a[j].y - a[i].y ;
change(dx , dy) ;
int bx = dx , by = dx * a[i].y - dy * a[i].x ;
change(bx , by) ;
pii a = make_pair(dx , dy) , b = make_pair(bx , by) ;
if(vis[make_pair(a , b)]) return 0 ;
else return 1 ;
}
void add(int i , int j)
{
int dx = a[j].x - a[i].x ;
int dy = a[j].y - a[i].y ;
change(dx , dy) ;
int bx = dx , by = dx * a[i].y - dy * a[i].x ;
change(bx , by) ;
pii a = make_pair(dx , dy) , c = make_pair(bx , by) ;
vis[make_pair(a , c)] = 1 ;
if(id[a] == 0) id[a] = ++ cur ;
b[id[a]] ++ ;
}
ll cal()
{
ll xie = 0 ;
for(int i = 1 ; i <= n ; i ++)
for(int j = i + 1 ; j <= n ; j ++)
{
if(a[i].x == a[j].x || a[i].y == a[j].y) continue ;
else if(!ok(i , j)) continue ;
else add(i , j) , xie ++ ;
}
return xie ;
}
ll solve()
{
ll sum = 0 ;
ll ans = 0 ;
for(int i = 1 ; i <= cur ; i ++) sum += b[i] ;
for(int i = 1 ; i <= cur ; i ++) ans += b[i] * (sum - b[i]) ;
return ans / 2 ;
}
int main()
{
scanf("%d" , &n) ;
for(int i = 1 ; i <= n ; i ++)
{
scanf("%d%d" , &a[i].x , &a[i].y) ;
a[i].x += 1e4 , a[i].y += 1e4 ;
}
sort(a + 1 , a + n + 1) ;
ll xie = 0 , heng = 0 , shu = 0 ;
for(int i = 1 ; i <= n ; i ++)
{
row[a[i].x].insert(a[i].y) ;
col[a[i].y].insert(a[i].x) ;
}
for(int i = 0 ; i <= 2e4 ; i ++)
{
heng += (row[i].size() > 1) ;
shu += (col[i].size() > 1) ;
}
xie = cal() ;
ll ans = 0 ;
ans += heng * shu ;
ans += heng * xie ;
ans += shu * xie ;
ans += solve() ;
printf("%lld\n" , ans) ;
return 0 ;
}
代码2(最简分数存储斜线)873ms:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
typedef pair<double , double> pdd ;
const int maxn = 1005 ;
const int maxm = 2e4 + 5 ;
set<int> row[maxm] , col[maxm] ;
int n ;
map<pdd , bool> vis ;
map<double , int> id ;
ll b[maxn * maxn] ;
int cur = 0 ;
struct node
{
int x , y ;
bool operator< (const node &s) const
{
if(y != s.y) return y < s.y ;
else return x < s.x ;
}
} a[maxn] ;
bool ok(int i , int j)
{
double dy = double(a[i].y - a[j].y) ;
double dx = double(a[i].x - a[j].x) ;
double k = dy / dx , b = double(a[i].y) - k * a[i].x ;
if(vis[make_pair(k , b)]) return 0 ;
else return 1 ;
}
void add(int i , int j)
{
double dy = double(a[i].y - a[j].y) ;
double dx = double(a[i].x - a[j].x) ;
double k = dy / dx , c = double(a[i].y) - k * a[i].x ;
vis[make_pair(k , c)] = 1 ;
if(id[k] == 0) id[k] = ++ cur ;
b[id[k]] ++ ;
}
ll cal()
{
ll xie = 0 ;
for(int i = 1 ; i <= n ; i ++)
for(int j = i + 1 ; j <= n ; j ++)
{
if(a[i].x == a[j].x || a[i].y == a[j].y) continue ;
else if(!ok(i , j)) continue ;
else add(i , j) , xie ++ ;
}
return xie ;
}
ll solve()
{
ll sum = 0 ;
ll ans = 0 ;
for(int i = 1 ; i <= cur ; i ++) sum += b[i] ;
for(int i = 1 ; i <= cur ; i ++) ans += b[i] * (sum - b[i]) ;
return ans / 2 ;
}
int main()
{
scanf("%d" , &n) ;
for(int i = 1 ; i <= n ; i ++)
{
scanf("%d%d" , &a[i].x , &a[i].y) ;
a[i].x += 1e4 , a[i].y += 1e4 ;
}
sort(a + 1 , a + n + 1) ;
ll xie = 0 , heng = 0 , shu = 0 ;
for(int i = 1 ; i <= n ; i ++)
{
row[a[i].x].insert(a[i].y) ;
col[a[i].y].insert(a[i].x) ;
}
for(int i = 0 ; i <= 2e4 ; i ++)
{
heng += (row[i].size() > 1) ;
shu += (col[i].size() > 1) ;
}
xie = cal() ;
ll ans = 0 ;
ans += heng * shu ;
ans += heng * xie ;
ans += shu * xie ;
ans += solve() ;
printf("%lld\n" , ans) ;
return 0 ;
}