Codeforces Round #558 (Div. 2) C2 - Power Transmission (Hard Edition) (two solutions)

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值