洛谷传送门
Codeforces传送门
题目大意
求 ∑ i = 1 n i k m o d    ( 1 0 9 + 7 ) \sum_{i=1}^ni^k\mod (10^9+7) ∑i=1nikmod(109+7), n ≤ 1 0 9 , k ≤ 1 0 6 n\le 10^9,k\le 10^6 n≤109,k≤106
解题分析
首先可以知道的是, 答案是一个 k + 1 k+1 k+1次, 关于 n n n的多项式, 于是我们只需要知道 k + 2 k+2 k+2项的值就好了。
考虑拉格朗日插值, 为了方便计算插前
k
+
2
k+2
k+2个自然数:
f
(
n
)
=
∑
i
=
1
k
+
2
f
(
i
)
∏
j
≠
i
,
j
∈
[
1
,
k
+
2
]
n
−
j
i
−
j
f(n)=\sum_{i=1}^{k+2}f(i)\prod_{j\ne i,j\in[1,k+2]}\frac{n-j}{i-j}
f(n)=i=1∑k+2f(i)j̸=i,j∈[1,k+2]∏i−jn−j
然后发现
i
−
j
i-j
i−j无非是一段从
−
1
-1
−1开始递减的序列的乘积和从
1
1
1开始递增的乘积, 上面可以预处理出所有的
n
−
i
n-i
n−i的乘积然后乘上一个单独的逆元,
f
(
i
)
f(i)
f(i)可以
O
(
N
)
O(N)
O(N)筛出来或偷懒
N
l
o
g
(
N
)
Nlog(N)
Nlog(N)算, 然后就做完了。
当 n ≤ k + 2 n\le k+2 n≤k+2的时候暴力算就好了, 否则会搞出0来。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define MOD 1000000007
#define MX 1005000
#define gc getchar()
#define ll long long
IN int fpow(R int base, R int tim)
{
int ret = 1;
W (tim)
{
if (tim & 1) ret = 1ll * ret * base % MOD;
base = 1ll * base * base % MOD, tim >>= 1;
}
return ret;
}
IN void add(int &tar, R int ad) {tar += ad; if (tar >= MOD) tar -= MOD;}
int n, bd, k, all, sum, ans;
int pos[MX], neg[MX];
int main(void)
{
scanf("%d%d", &n, &k); bd = k + 2;
if (n <= bd)
{
for (R int i = 1; i <= n; ++i) add(sum, fpow(i, k));
printf("%d\n", sum); return 0;
}
pos[0] = neg[0] = all = 1;
for (R int i = 1; i <= bd; ++i) all = 1ll * all * (n - i) % MOD;
for (R int i = 1; i <= bd; ++i) pos[i] = 1ll * pos[i - 1] * i % MOD;
for (R int i = 1; i <= bd; ++i) neg[i] = 1ll * neg[i - 1] * (MOD - i) % MOD;
for (R int i = 1; i <= bd; ++i)
{
add(sum, fpow(i, k));
add(ans, 1ll * sum * all % MOD * fpow(n - i, MOD - 2) % MOD * fpow(neg[bd - i], MOD - 2) % MOD * fpow(pos[i - 1], MOD - 2) % MOD);
}
printf("%d\n", ans);
}