リンク

http://d.hatena.ne.jp/semiexp/20111220/1324361030

問題概要

1^k+2^k+\cdots +n^k\mod Pを求めよ。

制約

n<=10^18, k<=100, 2<=P<=10^9

状態

('A`)

ポイント

いろいろな解き方があるのですべて紹介する。

解き方

+ ...

超愚直

O(nk). しぬ。

愚直

累乗を繰り返し2乗して求める。nが小さくkが大きい場合に有効。O(nlog k).

Pが大きい素数の場合

Pが大きい素数の場合、k乗和の公式を求めた後それに代入する。k乗和の公式を求めるのはO(k^3), そしてその公式はたかだかk+2次なので、O(klog k)、全体でO(k^3)で求められる。が、この公式には除算がはいっているため、Pが素数でないとき非常に難しくなる。
(x+1)^{k+1}=x^{k+1}+\binom{k+1}{1}x^k+\binom{k+1}{2}x^{k-1}+\cdots +\binom{k+1}{k+1}x^0から、
x^k=\frac{1}{k+1}\left( (x+1)^{k+1}-x^{k+1}-\left(\binom{k+1}{2}x^{k-1}+\cdots +\binom{k+1}{k+1}x^0\right)\right).これをx=1~nで足して
S(n,k)=\frac{1}{k+1}\left( (n+1)^{k+1}-1-\left(\binom{k+1}{2}S(n,k-1)+\cdots +\binom{k+1}{k+1}S(n,0)\right)\right)
=\frac{1}{k+1}((n+1)^{k+1}-1)-\left(\frac{1}{2}\binom{k}{1}S(n,k-1)+\cdots +\frac{1}{k+1}\binom{k}{k}S(n,0)\right)

semiexp式

二項定理より、
\begin{pmatrix}(i+1)^0\\(i+1)^1\\\vdots \\(i+1)^k\end{pmatrix}=\begin{pmatrix}\binom{0}{0}&amp;0&amp;\cdots &amp;0\\ \binom{1}{0}&amp;\binom{1}{1}&amp;\cdots &amp;0\\\vdots &amp;\vdots &amp;\ddots &amp;\vdots \\ \binom{k}{0}&amp;\binom{k}{1}&amp;\cdots &amp; \binom{k}{k}\end{pmatrix} \begin{pmatrix}i^0\\i^1\\\vdots \\i^k\end{pmatrix}が成り立つので、\begin{pmatrix}1\\0\\\vdots \\0\end{pmatrix}からはじめて行列(Pascal Matrix)をk乗すればよい。O(k^3log n).
Pascal Matrixは、k乗すると、各要素についてk^(対角成分からの距離)倍したのと同様の値になるので、これを利用するとO(k^2log n)でできる。

Loner式

http://community.topcoder.com/stat?c=problem_solution&cr=22285847&rd=12169&pm=8725
求める式をS(n,k)とする。nの繰り返し2乗法で求める。S(a,k)からS(a+1,k), S(a,0)~S(a,k)からS(2a,k)が求まれば良い。前者は簡単で、(a+1)^kを足すだけで良い。
S(2a,k)の(a+x)^kの項に注目すると、(a+x)^kは二項展開できて、
(a+x)^k=x^k+\binom{k}{1}ax^{k-1}+\binom{k}{2}a^2x^{k-2}+\cdots +\binom{k}{k}a^kx^0と表せる。これをx=1,2,...,aについて辺々足すと
S(2a,k)-S(a,k)=S(a,k)+\binom{k}{1}aS(a,k-1)+\binom{k}{2}a^2S(a,k-2)+\cdots +\binom{k}{k}a^kS(a,k-k)となる。

DPでもかける。直前の項しか参照しないのでDP用のメモリは2*(k+1)で済む。*2をメインにして+1があれば一律にプラスするようにして成長させていく。

コード

+ ...
public static long powerSum(long n, int k, int mod)
	{
		int[][] C = new int[k+1][k+1];
		for(int i = 0;i <= k;i++){
			C[i][0] = 1;
			for(int j = 1;j <= i;j++){
				C[i][j] = (C[i-1][j]+C[i-1][j-1])%mod;
			}
		}
 
		int u = 63-Long.numberOfLeadingZeros(n); // 監視対象ビット
		long[][] dp = new long[2][k+1];
		int cur = 1, prev = 0;
		Arrays.fill(dp[0], 1);
 
		u--;
		long c = 1; // もりもり成長
		while(u >= 0){
			// c->2c
			for(int i = 0;i <= k;i++){
				long ret = 2*dp[prev][i];
				long mul = c;
				for(int j = 1;j <= i;j++){
					ret += C[i][j]*mul%mod*dp[prev][i-j]%mod;
					mul = mul*c%mod;
				}
				dp[cur][i] = ret;
			}
			c *= 2;
 
			if(n<<63-u<0){
				// c->c+1
				c++;
				long plus = 1;
				for(int i = 0;i <= k;i++){
					dp[cur][i] += plus;
					plus = plus * c % mod;
				}
			}
			for(int i = 0;i <= k;i++){
				dp[cur][i] %= mod;
			}
			u--;
			cur ^= 1; prev ^= 1;
		}
		return dp[prev][k];
	}
 
最終更新:2012年03月24日 22:28