레이블이 정수론인 게시물을 표시합니다. 모든 게시물 표시
레이블이 정수론인 게시물을 표시합니다. 모든 게시물 표시

2018년 4월 6일 금요일

13997 이항 계수와 쿼리

13997 이항 계수와 쿼리 https://www.acmicpc.net/problem/13977

$M$개의 자연수 $N$과 정수 $K$가 주어질 때 $_{n}C_{r}$을 구하는 문제이다.

$O(N + log(p) + M)$에 해결할 수 있다.
$_{n}C_{r}$ = $n!/(r!*(n-r)!$ = $n!*(r!*(n-r)!)^{-1}$ = $n!*(r!)^{-1}*(n-r)!^{-1}$

$a^{p}$ $\equiv$ $a$ $(mod$ $p)$ ($a$는 자연수, $p$는 소수)
$a^{p - 1}$ $\equiv$ $1$ $(mod$ $p)$
$a^{p - 2}*a$ $\equiv$ $1$ $(mod$ $p)$ 즉, $a$의 역원은 $a^{p-2}$이다.
$a^{p-2}$는 분할정복으로 $log(p)$만에 구할 수 있고 역원들 또한 전처리해주어 $O(n)$에 해결할 수 있다.


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod (ll)(1e9 + 7)
const int MAXN = 4e6 + 6;
int n, r;
ll fact[MAXN];
ll inverse[MAXN];
ll mpow(ll a, ll p) {
    if (p == 0return 1;
    if (p == 1return a;
    if (p & 1return mpow(a, p - 1)*a % mod;
    ll tmp = mpow(a, p / 2);
    return tmp * tmp % mod;
}
void init() {
    fact[0= fact[1= 1;
    for (int i = 2; i < MAXN; i++) fact[i] = fact[i - 1* i % mod;
    inverse[MAXN - 1= mpow(fact[MAXN - 1], mod - 2);
    for (int i = MAXN - 2; i >= 1; i--) inverse[i] = inverse[i + 1* (i + 1) % mod;
    inverse[0= inverse[1= 1;
}
ll nCr(ll n, ll r) {
    return ((fact[n] * inverse[r] % mod) * inverse[n - r]) % mod;
}
int main(){
    init();
    int t;
    scanf("%d"&t);
    while (t--) {
        scanf("%d%d"&n, &r);
        printf("%lld\n", nCr(n, r));
    }
    return 0;
}
cs

2017년 6월 15일 목요일

3955 캔디 분배

백준 3955 캔디 분배 https://www.acmicpc.net/problem/3955

파티에 K명이 참가한다. 사탕을 구입하여 K명에게 공정하게 나눠줘야한다. 
그런데 한개를 잃어버리는 경우가 항상 있어서 1개를 추가 구입해야한다. 
한봉지에 사탕 C개가 들어있을 때 구입해야하는 사탕 봉지 개수를 구하는 문제이다. 
(단, $10^9$개 넘는 사탕 봉지는 못삼)

구입해야하는 사탕 봉지를 $y$라 하자.
그렇다면 다음과 같은 식을 유도할  수 있다.

①. $C*y=K*x+1$

양변을 K로 나누어 주면
②. $C*y%K =  1$

즉, $C*y \equiv 1$ (mod $K$)
어디서 많이 본 식이다. 그렇다.
C를 K로 나눈 나머지 연산의 곱셈에 대한 역원을 구하는 것이다.

Extended Euclidean Algorithm을 적용하면 쉽게 구할 수 있다.

C=1, K=1일때 예외처리를 잘 해주자.

#include <cstdio>
long long gcd(long long A, long long B){
    if (B == 0return A;
    return gcd(B, A%B);
}
long long extended_gcd(long long A, long long B){
    long long r, q, tmpA = A, t, t1 = 0, t2 = 1;
    while (B != 0){
        q = A / B;
        r = A%B;
        t = t1 - q*t2;
        A = B;
        B = r;
        t1 = t2;
        t2 = t;
    }
    while (t1<0) t1 += tmpA;
    return t1;
}
int main(){
    int T;
    scanf("%d"&T);
    while (T--){
        long long K, C;
        scanf("%lld%lld"&K, &C);
 
        if (C == 1){        // 0 이될 수 밖에 없음
            if (K + 1 > 1e9)
                printf("IMPOSSIBLE\n");
            else
                printf("%lld\n", K + 1);
            continue;
        }
        else if (K == 1){
            printf("1\n");
            continue;
        }
        else if (gcd(K, C) != 1){
            printf("IMPOSSIBLE\n");
            continue;
        }
        //   y:우리가 구하고자하는 답, x: K*x개를 사야함
        //     C*y = K*x + 1
        //   C*y - K*x = 1 or C*y % K = 1 (단 C,K는 서로소)
 
        long long ans = extended_gcd(K, C);
        if (ans > 1e9)
            printf("IMPOSSIBLE\n");
        else
            printf("%lld\n", ans);
    }
    return 0;
}
cs