Link:C2. Power Transmission (Hard Edition)
大致的思想是先求出所有的直线(去除重复直线),先算出两两相交答案,然后再减去平行的直线。
Solution1:
利用直线的斜截式(y = kx + b,应该是叫这个吧),求出每个的k, b,这样就可以轻松完成去重,平行也很好判
struct P{
db x, y;
}a[maxn];
struct line{
db k, d;
line(){}
line(db k, db d){
this->k = k, this->d = d;
}
bool operator < (const line &l) const{
return k != l.k ? k < l.k : d < l.d;
}
};
int n;
map<line, bool> M1;
map<db, ll> M2;
int main()
{
scanf("%d", &n);
rep(i, 1, n) cin >> a[i].x >> a[i].y;
ll tot = 0;
rep(i, 1, n) rep(j, 1, i - 1){
db k, d;
if(a[i].x == a[j].x) k = inf, d = a[i].x;
else {
k = (a[i].y - a[j].y) / (a[i].x - a[j].x);
d = a[i].y - a[i].x * k;
}
if(M1.find(line(k, d)) == M1.end()){
tot++;
M1[line(k, d)] = 1;
M2[k]++;
}
}
ll ans = tot * (tot - 1) / 2;
for(auto x : M2){
ans -= x.se * (x.se - 1) / 2;
}
printf("%lld\n", ans);
return 0;
}
Solution2:
Solution1太不优美了,涉及到浮点数运算,会担心运算、判断相等的时候失精。
所以用点向式来表示一个直线,斜率用一个dt_x, dt_y来表示,相对位置用叉积来算,这样就避免了浮点数运算
Tips:点向式大概就是一个向量表示方向,再过一个点,就能表示一条直线了
9:点向式:(x-x0)/u=(y-y0)/v (u≠0,v≠0)【适用于任何直线】
表示过点(x0,y0)且方向向量为(u,v )的直线
摘自百度百科
struct P{
int x, y;
}p[maxn];
struct line{
int dt_x, dt_y, dot;
line(){}
line(int dt_x, int dt_y, int dot){
this->dt_x = dt_x;
this->dt_y = dt_y;
this->dot = dot;
}
bool operator <(const line &l) const{
if(dt_x == l.dt_x){
if(dt_y == l.dt_y) return dot < l.dot;
return dt_y < l.dt_y;
}
return dt_x < l.dt_x;
}
};
set<line> s;
map<PII, ll> M;
int n;
int main()
{
scanf("%d", &n);
rep(i, 1, n) scanf("%d %d", &p[i].x, &p[i].y);
rep(i, 1, n){
rep(j, 1, i - 1){
PII p1 = mp(p[i].x, p[i].y), p2 = mp(p[j].x, p[j].y);
if(p1 < p2) swap(p1, p2);
int dt_x = p1.fi - p2.fi, dt_y = p1.se - p2.se;
int t = gcd(dt_x, dt_y);
dt_x /= t, dt_y /= t;
int dot = p1.fi * dt_y - p1.se * dt_x;
s.insert(line(dt_x, dt_y, dot));
}
}
ll tot = s.size();
tot = tot * (tot - 1) / 2;
for(auto x : s){
M[mp(x.dt_x, x.dt_y)]++;
}
for(auto x : M){
tot -= x.se * (x.se - 1) / 2;
}
printf("%lld\n", tot);
return 0;
}