Codeforces Round #558 (Div. 2) - C2 - Power Transmission (Hard Edition) 做题笔记

题目传送门
做到一半就放弃了,后来看了官方题解后被震惊到原来这题可以写起来这么简单,心有所感,必得记一下这题。

我们先来看一下这题需要我们做些什么吧,题目给出了n个点用坐标(x, y)表示,我们需要求出这些点构成的直线中有几对是相交的(两条为一对)。
那要怎么做呢?我们知道两点确定一条直线,但是三点以上落在同一直线上的可能是存在的,首先我们要求出总共有几条不同的直线,然后这样的一条直线与其他斜率不想等的直线必有交点,所以斜率也是需要考虑的。
这要怎么做?两个循环先找出两个不同的点确定一条直线,可以用ax - by = c这样来表示,我们有两个点(x1, y1),(x2, y2)那么

k ( x − x 1 ) = y − y 1 k(x-x1) =y-y1 k(xx1)=yy1
其中斜率 : k = ( y 1 − y 2 ) / ( x 1 − x 2 ) k = (y1-y2)/(x1-x2) k=(y1y2)/(x1x2)
所以 ( y 1 − y 2 ) x − ( y 1 − y 2 ) x 1 = ( x 1 − x 2 ) y − ( x 1 − x 2 ) y 1 (y1-y2)x-(y1-y2)x1=(x1-x2)y-(x1-x2)y1 (y1y2)x(y1y2)x1=(x1x2)y(x1x2)y1
( y 1 − y 2 ) x − ( x 1 − x 2 ) y = ( y 1 − y 2 ) x 1 − ( x 1 − x 2 ) y 1 (y1-y2)x-(x1-x2)y=(y1-y2)x1-(x1-x2)y1 (y1y2)x(x1x2)y=(y1y2)x1(x1x2)y1
于是我们可以看作 a = ( y 1 − y 2 ) , b = ( x 1 − x 2 ) , c = a ∗ x 1 − b ∗ y 1 a = (y1 - y2),b = (x1 - x2),c = a*x1 - b*y1 a=(y1y2)b=(x1x2)c=ax1by1

然后再加一重循环找到并去除在一条直线上的点?从这里开始就必T了好吗?然后记录当前直线的斜率,之后与其他直线讨论时需要判断斜率,怎么想怎么麻烦。
我来看看官方是什么神仙解法,首先分析是这样没错,但是用了个我很少用到的神器,stl中的mappair!(现在想起来好多神器真是没咋用过,set也是集训才开始学的)
pair<int, int> slope,用slope(a, b)来代表a和b表示出的斜率。
map<pair<int, int>, set< long long >>slope_map,用slope_map来表示pair里塞斜率slope(a, b),set里塞c,来表示直线。若斜率slope(a, b)的坐标里有同样的c存在过,那就不算生成新直线,没有这个c存在就修改答案并塞入c,并且这个c是在当前斜率的附属中的,与其他斜率相同但是c不同的直线平行不会相交,这在累计答案时需要注意。
剩下的就是注意在统计斜率时需要把不同点表示的同一斜率进行化简并统一。
代码说话吧:

#include<bits/stdc++.h>
#define maxn 1005
#define maxm 200005
#define FOR(a, b, c) for(int a=b; a<=c; a++)
#define hrdg 1000000007
#define inf 2147483647
#define llinf 9223372036854775807
#define ll long long
#define pi acos(-1.0)
#define ls p<<1
#define rs p<<1|1
using namespace std;

int n, x[maxn], y[maxn];
ll ret, total;
map<pair<int, int>, set<ll>> slope_map;		

inline int read()
{
    char c=getchar();long long x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

int gcd(int x, int y){ return y ? gcd(y, x%y) : x;}

int main()
{
    n = read();
    FOR(i, 1, n){
        x[i] = read();
        y[i] = read();
    }
    FOR(i, 1, n-1)
        FOR(j, i+1, n)		//遍历所有的两点对
        {
            int x1 = x[i], y1 = y[i], x2 = x[j], y2 = y[j];
            //构造一条线穿过(x1, y1)和(x2, y2)
            //构造公式使满足 ax - by = c
            int a = y1 - y2, b = x1 - x2;
            int d = gcd(a, b);
            a /= d, b /= d;
            if(a < 0 || (a == 0 && b < 0))
            {
                a = -a;
                b = -b;
            }
            //以上是化简斜率并统一写法
            pair<int, int> slope(a, b);		//这就是当前斜率了
            ll c = 1LL * a * x1 - 1LL * b * y1;
            if(!slope_map[slope].count(c))
            {
                total++;
                slope_map[slope].insert(c);
                ret += total - slope_map[slope].size();			//size是平行直线的数量
            }
        }
    cout<<ret<<endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值