본문 바로가기

카테고리 없음

백준 11500번: Polynomial

 

 

https://www.acmicpc.net/problem/11500

 

11500번: Polynomial

A polynomial \(f(k)\) of degree \(t\) with integral coefficients is given as \(f(k) = c_0 + c_1k + c_2k^2 + \cdots + c_tk^t\), where the coefficients \(c_0, \dots, c_t\) are all integers. Here, we are interested in the sum \(S(n)\) of \(f(0)\), \(f(1)\), .

www.acmicpc.net


전처리를 하면 쉬운데, 전처리 없이는 다소 복잡한 문제가 될 수 있을 것 같습니다.

 

일단 문제를 풀기 위해 주어진 식을 그대로 씁니다.

$$S(n) = \sum_{k=0}^{n} f(k) = \sum_{k=0}^{n} \sum_{i=0}^{t} c_{i} k^{i}$$

그런데 위를 잘 관찰하면 다음을 얻습니다. (잘 안 보일 경우 시그마로 묶인 식을 전부 써보면 쉬워집니다.)

$$S(n) = \sum_{i=0}^{t} \left \{ c_{i} \sum_{k=0}^{n} k^{i} \right \}$$

이로부터 문제가 exploit하라고 제공한 식을 쓸 여지가 보입니다.

 

이에 관한 설명은 1492번: 합 에서 작성하였고, 결과만을 보여드리자면

$$A(n,k) = \sum_{i=0}^{n} i^{k}$$

일 때 $A(n,k)$에 관한 점화식이 다음과 같습니다.

$$A(n,k) = \frac{ (n+1)^{k} - 1 - \sum_{i=0}^{k-1} \binom{k+1}{i} A(n,i) }{ \binom{k+1}{k} }$$

이로부터 $S(n)$을 다음과 같이 표현할 수 있습니다.

$$S(n) = \sum_{i=0}^{t} c_{i} A(n,i) $$

따라서 문제는 $A(n,i)$을 $n$에 관한 $i+1$차의 다항식으로 나타내면 해결됩니다. 점화식은 이미 나와있으므로 식 정리를 잘 해보면 해결될 것입니다. (제가 이 방향으로 풀이를 완성하지 않아서 이 풀이는 여기서 마무리하려합니다.)


아마도 정해는 위와 같겠지만, Bernoulli's Number $B_{n}$을 도입하면 보다 편한 풀이가 가능합니다.

 

위키피디아에 따르면, Faulhaber's Formula는 다음과 같이 표현할 수 있습니다. (단, $B_{1}^{+} = \frac{1}{2}$의 convention을 사용했습니다.)

$$ \sum_{k=1}^{n} k^{p} = \frac{1}{p+1} \sum_{j=0}^{p} \binom{p+1}{j} B_{j} n^{p+1-j} $$

이 방법이 정해보다 복잡할 순 있겠지만, 로컬에서 각 $B_{j}$를 구했을 때 식이 더 간단해지는 것 같습니다.

 

링크 에 따르면, $B_{1}^{+} = \frac{1}{2}$을 채택했을 때

$$ B_{m}^{+} = 1 - \sum_{k=0}^{m-1} \binom{m}{k} \frac{B_{k}^{+}}{m-k+1} $$

이므로, 식을 그대로 잘 구현하면 로컬에서 $B_{j}$을 구할 수 있습니다.

 

다만, Bernoulli's Number은 잘 알려진 수열이므로 인터넷에서 값을 쉽게 찾을 수 있습니다.

 

그러면 Bernoulli's Number을 도입했을 때 $S(n)$의 $n^{i}$의 coefficient가 어떻게 나타날 지 전개해보겠습니다.

$$ S(n) = \sum_{i=0}^{t} \left \{ c_{i} \sum_{j=0}^{n} j^{i} \right \} $$

$$ = c_{0}\sum_{j=0}^{n} j^{0} + \sum_{i=1}^{t} \left \{ c_{i} \sum_{j=0}^{n} j^{i} \right \} $$

$$ = c_{0} + c_{0} \sum_{j=1}^{n} j^{0} + \sum_{i=1}^{t} \left \{ c_{i} \sum_{j=1}^{n} j^{i} \right \} $$

$$ = c_{0} + \sum_{i=0}^{t} \left \{ c_{i} \sum_{j=1}^{n} j^{i} \right \} $$

$$ = c_{0} + \sum_{i=0}^{t} \left \{ c_{i} \times \frac{1}{i+1} \sum_{j=0}^{i} \binom{i+1}{j} B_{j}^{+} n^{i+1-j} \right \} $$

$$ = c_{0} + \sum_{i=1}^{t+1} \left \{ n^{i} \sum_{j=0}^{t+1-i} \frac{ \binom{j+i}{j} c_{j+i-1} }{j+i} \times B_{j}^{+} \right \} $$

(편의상 $0^{0}=1$의 convention을 사용했습니다.)

 

이제 Bernoulli's Number와 Binomial Coefficient를 전처리하면 각 케이스 당 시간복잡도 $O(t^{2})$에 문제가 해결됩니다. $a_{0}=c_{0}$임은 자명합니다.

 

제 코드는 다음과 같습니다.

#include <stdio.h>

long long ABS (long long a);
long long GCD (long long a, long long b);

typedef struct {
	long long numerator;
	long long denominator;
} fraction;

fraction reduction (fraction A);
fraction addition (fraction A, fraction B);
fraction multiply (fraction A, fraction B);

int main(void) {
	
	long long binomial[27][27] = {{}};
	for (int i = 0; i <= 26; i++) {
		binomial[i][0] = binomial[i][i] = 1;
		for (int j = 1; j < i; j++) {
			binomial[i][j] = binomial[i-1][j] + binomial[i-1][j-1];
		}
	}
	
	fraction Bernoulli_plus[27] =
	{
		{1, 1},             // 0
		{1, 2},             // B+(1) = 1/2 and B-(1) = -1/2
		{1, 6},             // 2
		{0, 1},
		{-1, 30},           // 4
		{0, 1},
		{1, 42},            // 6
		{0, 1},
		{-1, 30},           // 8
		{0, 1},
		{5, 66},            // 10
		{0, 1},
		{-691, 2730},       // 12
		{0, 1},
		{7, 6},             // 14
		{0, 1},
		{-3617, 510},       // 16
		{0, 1},
		{43867, 798},       // 18
		{0, 1},
		{-174611, 330},     // 20
		{0, 1},
		{854513, 138},      // 22
		{0, 1},
		{-236364091, 2730}, // 24
		{0, 1},
		{8553103, 6}        // 26
	};
	
	int T;
	scanf("%d", &T);
	while(T--) {
		int t;
		scanf("%d", &t);
		
		int array[26];
		for (int i = 0; i <= t; i++) {
			scanf("%d", &array[i]);
		}
		
		fraction result[27] = {{}};
		for (int i = 1; i <= t + 1; i++) {
			result[i] = reduction((fraction){binomial[i][0] * array[i-1], i});
			for (int j = 1; j + i <= t + 1; j++) {
				result[i] = addition(result[i],
				                     multiply((fraction){binomial[j+i][j] * array[j+i-1], j+i}, Bernoulli_plus[j]));
			}
		}
		
		long long answer = ABS(array[0]);
		for (int i = 1; i <= t + 1; i++) {
			answer += ABS(result[i].numerator);
		}
		printf("%lld\n", answer);
	}
	return 0;
}

long long ABS (long long a) {
	return a > 0 ? a : -a;
}

long long GCD (long long a, long long b) {
	while(b) {
		long long temp = a % b;
		a = b;
		b = temp;
	}
	return a;
}

fraction reduction (fraction A) {
	long long gcd = GCD(ABS(A.numerator), A.denominator);
	A.numerator /= gcd;
	A.denominator /= gcd;
	return A;
}

fraction addition (fraction A, fraction B) {
	return reduction((fraction)
    {
        A.numerator * B.denominator + B.numerator * A.denominator,
        A.denominator * B.denominator
    }
    );
}

fraction multiply (fraction A, fraction B) {
	return reduction((fraction)
    {
        A.numerator * B.numerator,
        A.denominator * B.denominator
    }
    );
}

(C11, 0ms, 1116KB, 제출번호 55701150)


Bernoulli's Number을 쓸 때의 장점이 있다면, 자료형의 크기가 어느정도까지 커져야할지 예측할 수 있다는 것입니다.

 

$t \leq 25$가 주어졌으므로 binomial coefficient의 최댓값은 $\binom{26}{13}=10400600$이며, $B_{n}^{+}$의 분모/분자 중 그 크기가 가장 큰 것은 $B_{24}^{+}$의 분자로, -236364091입니다. 따라서 주어진 범위에서 모든 $a_{i}$와 $b_{i}$ 값을 64비트 정수로 표현할 수 있습니다.

$$ |a_{i}| < (t+1) \times \binom{t+1}{\frac{t+1}{2}} \times c_{i} \times | \textrm{numerator}[B_{n}^{+}] | \leq 26 \times \binom{26}{13} \times 10 \times 236364091 < < 2^{63}-1$$

 

분모의 경우, $B_{n}$의 성질 중 $B_{2k+1}=0$을 활용하면, $b_{i} < 27!! \times 2730 < 2^{63}-1$ 입니다. (단, !! 기호는 double factorial 입니다.)