题目大意:平面图上有n个点,现在任意两个点间连一条直线,问相交的直线有多少对?
分析:注意是直线,直线只要不平行就是相交的,于是想到总答案 扣去 平行线的贡献,要求总答案就要求线的条数,因为直线可能重叠。由y = k * x + b可知,只要确定k和b,就可以唯一确定一条直线,对每一个k开一个桶,把不重复的b放到桶内,就可以计算得到不重叠的总线数量和平行的线数量,答案就很好求了。但k 和b均可能是小数,如何映射。
增长经验的时候到了:前面做过类似的映射k,可以将k以最简分数形式约分后用map存储分子分母,如果不考虑b,k这样映射就完美了。考虑b的话,将直线分类处理,平行轴的线b只和x值或y值有关(平行于x轴时只和y的值有关,平行于y轴时只和x的值有关),不平行轴的线,斜率一定存在且不为0,这时可以移项通分,然后用和映射k类似的方式映射b即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int x[maxn],y[maxn],n;
int gcd(int a,int b) {
return !b ? a : gcd(b,a % b);
}
map<pair<int,int>,int>mp,tp;
set<int> line[maxn * maxn];
void solve(int &x,int &y) {
if(!x) x = 1;
if(!y) y = 1;
}
int main() {
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%d%d",&x[i],&y[i]);
int tot = 0,cnt = 0;
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
int dy = y[j] - y[i],dx = x[j] - x[i];
int g = gcd(dx,dy);
pair<int,int> tmp,cur;
if(dx == 0) {
tmp = pair<int,int>(0,1);
cur = pair<int,int>(x[i],x[i]);
}
else if(dy == 0) {
tmp = pair<int,int>(1,0);
cur = pair<int,int>(y[i],y[i]);
}
else {
tmp = pair<int,int>(dx / g,dy / g);
int p = x[j] * y[i] - y[j] * x[i];
int g2 = gcd(p,dx);
cur = pair<int,int>(p / g2,dx / g2);
}
if(!tp[cur]) tp[cur] = ++cnt;
if(!mp[tmp]) mp[tmp] = ++tot;
line[mp[tmp]].insert(tp[cur]);
}
}
long long ans = 0;
long long sum = 0;
for(int i = 1; i <= tot; i++) {
sum += line[i].size();
ans -= 1ll * line[i].size() * (line[i].size() - 1) / 2;
}
//cout << sum << endl;
ans += 1ll * sum * (sum - 1) / 2;
cout << ans << endl;
return 0;
}