Codeforces Round #558 (Div. 2) C:Power Transmission(直线映射)

27 篇文章 0 订阅

题目大意:平面图上有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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值