티스토리 뷰

[CF 1163C2] Power Transmission (Hard Edition) - 2000


문제 설명 : 


풀이 :


주의할 점 : 


배울 점 : 

2차원 좌표평면 상의 직선을 표현하기 위해서는 일반적으로 기울기와 y절편이 필요하다. 그런데 기울기나 y절편 모두 정수가 아닌 유리수 형태일 수도 있다. c++에서는 소수의 정밀도 문제 때문에 유리수를 그대로 사용하는 것은 위험하다. 따라서 이런 경우에는 일반적으로 두 정수로 표현되는 기약분수 꼴로 나타낸다. 여기서 나는 기울기와 y절편 모두 유리수가 될 수 있으므로 (기울기의 분모,기울기의 분자, y절편의 분모, y절편의 분자) 총 4개의 dimension이 필요할 것 같아서 너무 복잡하다고 생각했다. 그런데 사실 일차함수에서는 단 3개면 된다. 

우리가 일반적으로 알고있는 y=ax+b형태에서 기울기 a는 (y1-y2) / (x1-x2)로 표현된다. 이를 대입해보면 y=(y1-y2)/(x1-x2)*x+b이고 이항해서 정리하면 (x2-x1)y-*(y2-y1)x=b형태가 된다. 즉 b를 굳이 유리수 형태가 아닌 정수 형태로 표현이 가능하다는 것이다. 따라서 단 3개의dimension으로 모든 일차함수 형태를 표현할 수 있는 것이다. 이때의 b를 c라고 하자. 그러면 dx(=x1-x2),dy(y1-y2),c의 gcd1를 구해서 각자 모두 나눠서 서로소 꼴로 만들어준다. 그 3개의 값을 하나로 묶어서 set에다가 insert하는 것이다. 그리고 기울기는 같지만 c값이 다른 직선들도 별도로 카운트해야하므로 이번에는 dx,dy만의 gcd2를 구해서 나눠준 다음 이 값을 map<pair<int,int>,int>에 넣어준다.

최종적으로 정답은 set에 들어있는 원소의 수=m, map의 각 기울기 별 원소의 수를 k_i라고 했을 때 "mC2 - sum of (k_iC2) on every i" 이다.


이번에 처음으로 tuple헤더를 써 보았다. tuple은 pair의 확장으로 생각하면 된다. pair는 멤버를 단 두개밖에 넣지 못했지만 tuple같은 경우는 3개 이상도 넣을 수 있다. 사용하는 방법도 pair를 쓸 때처럼 {}안에다가 원소를 넣어주면 된다. tuple을 사용하게 되면 3개 이상의 수를 비교해야 할 때, 기존에는 min(min(a,b,),c)처럼 min안에 min을 넣어야 했지만 tuple을 사용하면 min({a,b,c})이렇게 적기만 해도 되어서 간단하다.


코드:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<iostream>
#include<functional>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<assert.h>
#include<stdlib.h>
#include<stack>
#include<tuple>
using namespace std;
/*********************Contest Template***********************/
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<long long,long long> pll;
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(0);
struct S{
    int a,b; S(){}S(int _a,int _b)
    { a=_a; b=_b; }
    const bool operator<(const S &o) const{
        return a<o.a;}
};
string exm;
inline void exf(void){ cout<<exm<<"\n"; exit(0); }
template <typename T>
inline void showAll(vector<T> &v,string sep=""){
    for(T &here:v) cout<<here<<sep;}
template <typename T>
inline vector<int> int_seperation(T N,int d=10){
    vector<int> v; while(N){v.push_back(N%d); N/=d;}
    reverse(v.begin(),v.end()); return v; }
/*********************Contest Template***********************/
const int SIZE= 1009;
 
int x[SIZE],y[SIZE];
 
int getGCD(int a,int b){
    return (b==0)?a:getGCD(b,a%b);
}
 
int main(){
    int n;  scanf("%d",&n);
 
    for(int i=1 ; i<=n ; i++){
        scanf("%d %d",&x[i],&y[i]);
    }
 
    map<pii,int> mp;
    set<tuple<int,int,int>> st;
 
    for(int i=1 ; i<=n ; i++){
        for(int j=i+1 ; j<=n ; j++){
            int dx=x[i]-x[j],dy=y[i]-y[j];
            int c=dx*y[i]-dy*x[i];
 
            int gcd1=getGCD(dx,dy);
            int gcd2=getGCD(getGCD(dx,dy),c);
 
            printf("%d %d %d %d\n",dx,dy,c,gcd2);
            if(st.count({dx/gcd2,dy/gcd2,c/gcd2})) continue;
            st.insert({dx/gcd2,dy/gcd2,c/gcd2});
            mp[{dx/gcd1,dy/gcd1}]++;
        }
    }
 
    ll m=st.size();
    ll ans=m*(m-1)/2;
 
    for(auto &here:mp){
        int sec=here.second;
        ans-=1LL*sec*(sec-1)/2;
    }
 
    printf("%lld\n",ans);
 
    return 0;
}
/*
*/
cs


'Problem Solving > Geometry' 카테고리의 다른 글

[Codeforces 1028C] Rectangles  (0) 2019.05.05
댓글