ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 4. 정수론-소인수분해, gcd, lcm, sieve
    SCCC - Soongsil Computing Contest Club/2020 Winter-Study 2020. 1. 14. 02:51
    반응형

    4.md
    0.01MB

    1. 복습
    1. mod n
    • 덧셈

    7 = 3 (mod 4)

    ex) a+b = (a+b)%n (mod n)

    7 + 9 = 16 = 0 (mod 4)

    3 + 1 = 4 = 0 (mod 4)

    12312312 + 234234234 = 0 + 2 = 2 (mod 4)

    • 곱셈

    7*9 = 63 = 3 (mod 4)

    3 * 1 = 3 (mod 4)

    즉, 미리 mod를 적용해주면 식이 간단해진다.

    답이 심각하게 커지는 경우, mod를 많이 쓰며 나중에 다이나믹 프로그래밍에서 많이 나온다.

    참고 문제 : [boj.kr/17466]

    1. 소인수 분해

    어떤 수를 소인수 분해 하려면, 2부터 sqrt(N)까지 원소들의 조건을 따지면서 분해하면 된다.

    (1) N이 합성수인 경우, N = p1^r1 * p2^r2 * p3^r3 * ... * pk^rk

    N = 2^r1 * 3^r2 * 5^r3 * p4^r4

    • 어떤 N이 오직 sqrt(N)보다 작은 소인수로 이루어짐

    이때는 자명하므로 패스.

    • 어떤 N을 소인수 분해했을 때 sqrt(N)보다 큰 소인수도 있음

    sqrt(N)보다 큰 소인수는 지수가 무조건 1이다. (하나밖에 없다) => 지수가 2개 이상이면 그 2개만으로 N을 넘어버리기 때문에 1개밖에 없다.

    (2) N이 소수인 경우, 어떤 N을 소인수 분해했을 때 sqrt(N)보다 큰 소인수도 있다는 조건과 같음. 따라서 예외처리할 필요 없음.

    #include <iostream>
    #include <vector>
    using namespace std;
    vector<pair<int, int> > v;
    int main(){
        int N; cin >> N;
        for(int p=2; p*p<=N; ++p){
            int cnt=0;
            while(N%p==0){
                N/=p;
                cnt++;
            }
            if(cnt!=0) v.push_back(make_pair(p, cnt));
        }
        if(N != 1) {
            v.push_back(make_pair(N, 1));
        }
        cout << "N의 소인수분해" << '\n';
        for(int i=0; i<v.size(); ++i){
            cout << v[i].first << '^' << v[i].second << '\n';
        }
    }

    참고 문제 : [boj.kr/7806, boj.kr/2312]

    1. 최대공약수 gcd(greatest common divisor)

    최대공약수를 소인수 분해를 이용해서 하면, 시간이 매우 오래걸린다.

    따라서 유클리드 호제법을 쓴다.

    gcd(35, 25) = gcd(25, 10) = gcd(10, 15) = gcd(15, 10) = gcd(10, 5) = gcd(5, 5) = gcd(5,0) = 5.

    이걸 살짝 변형해서 현대에는

    gcd(35, 25) = gcd(25, 10) = gcd(10, 5) = gcd(5, 0) = 5.

    식 : gcd(a, b) = gcd(b, a%b)이고, gcd(k, 0)일 때 답은 k ( 단, a, b, k가 양의 정수일 때)

    int gcd(int a, int b){
        if(b == 0) return a;
        return gcd(b, a%b);
    }

    재귀함수로 구현 가능하다.

    팁1 : 시간복잡도는 최악 O(log_10(N))이다. 하지만 정말 빠르기 때문에 O(1)이라고 과장해서 말할 수도 있다.

    팁2 : a가 b보다 작아도 예외처리 안해도 된다.

    1. 최소공배수 lcm(least common multiple)

    식은 lcm(a, b) = a/gcd(a,b) * b

    long long lcm(int a, int b) {
      return a/gcd(a, b) * b;
    }

    팁1 : 오버플로우 조심해야한다.

    팁2 : b/gcd(a,b) * a해도 동일하게 나온다.

    참고 문제 : [boj.kr/2609, boj.kr/9613, boj.kr/1735]

    1. 에라토스테네스의 체(sieve)

    소수 구하는 알고리즘. 2부터 sqrt(N)까지 i*i ~ N 구간 내에 있는 i의 모든 배수를 지운다. 끝나고 남은 숫자들이 모두 소수.

    #include <iostream>
    #include <vector>
    using namespace std;
    const int MAX_N = 1e6; // 백만
    bool isErased[MAX_N+1];
    // sieve[p] == false : p가 아직 안지워졌다.
    // sieve[p] == true  : p가 지워졌다.
    int main(){
        int N; cin >> N;
    
        isErased[0] = true;
        isErased[1] = true;
        for(int p=2; p*p <=N; ++p){
            if(isErased[p]) continue;
            for(int j=p*p; j<=N; j+=p){
                isErased[j]=true;
            }
        }
        while(true){
            int K; cin >> K;
            if(isErased[K]) cout << "소수 아님" << '\n';
            else cout << "소수!" << '\n';
        }
    }

    시간 복잡도는 O(N * log(log N)). 사실상 O(N)

    참고 문제 : [boj.kr/1978, boj.kr/1929, boj.kr/4948, boj.kr/1456, boj.kr/1676, boj.kr/2004]

    1. 부록 : 참고 문제 풀이
    • boj.kr/17466
    #include <iostream>
    using namespace std;
    int main(){
        ios_base::sync_with_stdio(false); cin.tie(NULL);
    
        int N, P; cin >> N >> P;
        int res = 1;
        for(int i=1; i<=N; ++i){
            res = (res * (long long)i) % P;
    
    
        }
        cout << res << '\n';
    }
    • boj.kr/7806
    #include <iostream>
    #include <vector>
    using namespace std;
    vector<pair<int, int> > v;
    int main(){
        ios_base::sync_with_stdio(false); cin.tie(NULL);
        int N; cin >> N;
        for(int p=2; p*p<=N; ++p){
            int cnt=0;
            while(N%p==0){
                N/=p;
                cnt++;
            }
            if(cnt!=0) v.push_back(make_pair(p, cnt));
        }
        if(N != 1) {
            v.push_back(make_pair(N, 1));
        }
        for(int i=0; i<v.size(); ++i){
            for(int j=0; j<v[i].second; ++j){
                cout << v[i].first << '\n';
            }
        }
    }
    • boj.kr/2312
    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main(){
        ios_base::sync_with_stdio(false); cin.tie(NULL);
        int T; cin >> T;
        while(T--){
            vector<pair<int, int> > v;
            int N; cin >> N;
            for(int p=2; p*p<=N; ++p){
                int cnt=0;
                while(N%p==0){
                    N/=p;
                    cnt++;
                }
                if(cnt!=0) v.push_back(make_pair(p, cnt));
            }
            if(N != 1) {
                v.push_back(make_pair(N, 1));
            }
            for(int i=0; i<v.size(); ++i){
                cout << v[i].first << ' ' << v[i].second << '\n';
            }
        }
    }
    • boj.kr/2609
    #include <iostream>
    using namespace std;
    int gcd(int a, int b){
        if(!b) return a;
        return gcd(b, a%b);
    }
    long long lcm(int a, int b){
        return a/gcd(a, b) * b;
    }
    int main(){
        ios_base::sync_with_stdio(false); cin.tie(NULL);
        int a, b; cin >> a >> b;
        cout << gcd(a,b) << '\n' << lcm(a,b) << '\n';
    }
    • boj.kr/9613
    #include <iostream>
    using namespace std;
    typedef long long int ll;
    ll gcd(int a, int b){
        if(b==0) return a;
        return gcd(b, a%b);
    }
    
    int main(){
        ios_base::sync_with_stdio(false); cin.tie(NULL);
        ll t; cin >> t;
        while(t--){
            ll n; cin >> n;
            ll arr[101];
            for(int i=0; i<n; ++i){
                cin >> arr[i];
            }
            ll res=0;
            for(int i=0; i<n-1; ++i){
                for(int j=i+1; j<n; ++j){
                    res+=gcd(arr[i], arr[j]);
                }
            }
            cout << res << '\n';
        }
    }
    • boj.kr/1735
    #include <iostream>
    using namespace std;
    int gcd(int a, int b){
        if(!b) return a;
        return gcd(b, a%b);
    }
    int lcm(int a, int b){
        return a/gcd(a,b) * b;
    }
    int main(){
        ios_base::sync_with_stdio(false); cin.tie(NULL);
        int a, b, c, d; cin >> a >> b >> c >> d;
        int mo = lcm(b, d);
        int ja = lcm(b, d)/d*c + lcm(b,d)/b*a;
    
        cout << ja/gcd(mo, ja) << ' ' << mo/gcd(mo, ja) << '\n';
    }
    • boj.kr/1978
    #include <iostream>
    using namespace std;
    const int MAX_N = 1001;
    bool isErased[MAX_N+1];
    
    int main(){
        ios_base::sync_with_stdio(false); cin.tie();
    
        isErased[0] = true;
        isErased[1] = true;
        for(int p=2; p*p <=1000; ++p){
            if(isErased[p]) continue;
            for(int j=p*p; j<=1000; j+=p){
                isErased[j]=true;
            }
        }
    
        int N; cin >> N;
        int cnt=0;
        while(N--){
            int a; cin >> a;
            if(!isErased[a]) cnt++;
        }
    
        cout << cnt << '\n';
    }
    • boj.kr/1929
    #include <iostream>
    using namespace std;
    const int MAX_N = 1000001;
    bool isErased[MAX_N+1];
    
    int main(){
        ios_base::sync_with_stdio(false); cin.tie();
    
        isErased[0] = true;
        isErased[1] = true;
        for(int p=2; p*p <=MAX_N; ++p){
            if(isErased[p]) continue;
            for(int j=p*p; j<=MAX_N; j+=p){
                isErased[j]=true;
            }
        }
        int a, b; cin >> a >> b;
        for(int i=a; i<=b; ++i){
            if(!isErased[i]) cout << i << '\n';
        }
    }
    • boj.kr/4948
    #include <iostream>
    using namespace std;
    const int MAX_N = 247000;
    bool isErased[MAX_N+1];
    
    int main(){
        ios_base::sync_with_stdio(false); cin.tie();
    
        isErased[0] = true;
        isErased[1] = true;
        for(int p=2; p*p <=MAX_N; ++p){
            if(isErased[p]) continue;
            for(int j=p*p; j<=MAX_N; j+=p){
                isErased[j]=true;
            }
        }
        while(1){
            int a, cnt=0; cin >> a;
            if(a==0) break;
            for(int i=a+1; i<=2*a; ++i){
                if(!isErased[i]) cnt++;
            }
            cout << cnt << '\n';
        }
    }
    • boj.kr/1456 ( long long 범위 벗어나는거 조심)
    #include <iostream>
    #include <cmath>
    typedef long long ll;
    using namespace std;
    const int MAX_N = 1e7;
    bool isErased[MAX_N+1];
    int main(){
        ll a, b;
        cin >> a >> b;
        isErased[0] = true;
        isErased[1] = true;
        for(ll p=2; p<=MAX_N; ++p){
            if(isErased[p]) continue;
            for(ll j=p*p; j<=MAX_N; j=j+p){
                isErased[j]=true;
            }
        }
        ll cnt = 0;
        ll aa = sqrt(a);
        for(ll p=2; p*p<=b; p=p+1){
            if(!isErased[p]) {
                for(ll k=p; p<=b/k; k=k*p){
                    if(k*p>=a) cnt = cnt + 1;
                }
            }
        }
        cout << cnt << '\n';
    }
    • boj.kr/1676
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int main(){
        ios_base::sync_with_stdio(false); cin.tie(NULL);
        int N;
        cin >> N;
        int k=2;
        int cnt2=0, cnt5=0;
        while(k<=N){
            cnt2+=N/k;
            k*=2;
        }
        k=5;
        while(k<=N){
            cnt5+=N/k;
            k*=5;
        }
        cout << min(cnt2, cnt5) << '\n';
    }
    • boj.kr/2004
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    ll cnt2=0, cnt5=0;
    void f(ll N){
        ll k=2;
        while(k<=N){
            cnt2+=N/k;
            k*=2;
        }
        k=5;
        while(k<=N){
            cnt5+=N/k;
            k*=5;
        }
    }
    void f2(ll N1, ll N2){
        ll k=2;
        while(k<=N1){
            cnt2-=N1/k;
            k*=2;
        }
        k=2;
        while(k<=N2){
            cnt2-=N2/k;
            k*=2;
        }
        k=5;
        while(k<=N1){
            cnt5-=N1/k;
            k*=5;
        }
        k=5;
        while(k<=N2){
            cnt5-=N2/k;
            k*=5;
        }
    }
    int main(){
        ios_base::sync_with_stdio(false); cin.tie(NULL);
        ll n, m;
        cin >> n >> m;
    
        f(n);
        f2(m, n-m);
        if(min(cnt2, cnt5)<=0){
            cout << '0' << '\n';
        }
        else {
            cout << min(cnt2, cnt5) << '\n';
        }
    }
    반응형

    'SCCC - Soongsil Computing Contest Club > 2020 Winter-Study' 카테고리의 다른 글

    6. dp : LIS, Knapsack  (0) 2020.01.16
    5. 1차원, 2차원 dp  (0) 2020.01.15
    3. heap, priority_queue, set, map  (0) 2020.01.09
    2. pair, sort, binary search  (0) 2020.01.08
    1. c++ 기본  (0) 2020.01.06

    댓글

Designed by Tistory.