51nod 1258 序列求和(拉格朗日插值||ntt多项式求逆)求解自然数幂和问题

**

1258 序列求和 V4

**

8.0 秒 131,072.0 KB 1280 分 9级题
T(n) = n^k,S(n) = T(1) + T(2) + … T(n)。给出n和k,求S(n)。
例如k = 2,n = 5,S(n) = 1^2 + 2^2 + 3^2 + 4^2 + 5^2 = 55。
由于结果很大,输出S(n) Mod 1000000007的结果即可。

输入
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 500)
第2 - T + 1行:每行2个数,N, K中间用空格分割。(1 <= N <= 10^18, 1 <= K <= 50000)

输出

共T行,对应S(n) Mod 1000000007的结果。

输入样例
3
5 3
4 2
4 1
输出样例
225
30
10

首先看第一种方法根据差分法的定义
在这里插入图片描述
**自然数幂和一定可以构成一个k+1次的多项式(若次方数为k)
所以可以先求出前k+2项的函数值 由于 k+2个点必定可以确定一条最高次幂为k+1次方的函数
所以此时可以直接套用拉格朗日插值求出第n项 **
注意的是当n比较大时 i^k%mod必定为零所以n%=mod可以减少时间

插值法代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 5;
const int N = 5e4;
const int mod = 1e9 + 7;
ll Pow(ll x, ll y, ll mod){
	ll res = 1;
	while (y){
		if (y & 1) res = res*x%mod;
		y >>= 1;
		x = x*x%mod;
	}
	return res;
}
ll fac[maxn], f[maxn], n, k;
void init(){ //sgm(i^k)求和的第n项
	fac[0] = 1; f[0] = 0;
	for (int i = 1; i <= N; i++){
		fac[i] = fac[i - 1] * i%mod;//阶乘
		//f[i] = (f[i - 1] + Pow(i, k, mod)) % mod;//前k项的函数值y
	}
}
ll p[maxn], q[maxn];//正反求分母的式子 
ll solve(ll n, ll k){
	ll ans = 0;
	p[0] = q[k + 3] = 1;
	for (int i = 1; i <= k + 2; i++) p[i] = p[i - 1] * (n - i) % mod;
	for (int i = k + 2; i >= 1; i--) q[i] = q[i + 1] * (n - i) % mod;
	for (int i = 1; i <= k + 2; i++){
		ll x = (k - i + 2) % 2 ? (-1) : 1;
		ll num = f[i] * p[i - 1] % mod * q[i + 1] % mod;//函数值乘分子
		ll c=Pow(fac[i - 1] * fac[k - i + 2] % mod, mod - 2, mod) % mod;//分母
		ans = (ans + (num*c%mod*x%mod + mod) % mod) % mod;
	}
	return ans;
}
int main(){
	init();
	int T;
	cin >> T;
	while (T--){
		scanf("%lld%lld", &n, &k);
		n %= mod;
		for (int i = 1; i <= k + 2; i++)
			f[i] = (f[i - 1] + Pow(i, k, mod)) % mod;//前k项的函数值y
		printf("%lld\n", solve(n, k));
	}
	return 0;
}

用ntt多项式求逆做法:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int G = 3, mod = 1e9 + 7, N = (1<<17);// mod 998244353
int n, len, a[N], rev[N];
int B[N], f[N], inv[N], c[N];
const int mod1 = 998244353, mod2 = 469762049, mod3 = 1004535809;
int m[3] = { mod1, mod2, mod3 };
ll M = 1ll * mod1*mod2;
ll Pow(ll x, ll y, ll mod){
	ll res = 1;
	while (y){
		if (y & 1) res = res*x%mod;
		y >>= 1;
		x = x*x%mod;
	}
	return res;
}
ll mul(ll x, ll y, ll mod){
	ll res = 0;
	while (y){
		if (y & 1) res = (res + x) % mod;
		x = x * 2 % mod;
		y >>= 1;
	}
	return res;
}
ll C(ll n, ll m){
	if (m > n) return 0;
	return 1ll*f[n] * inv[n - m] % mod*inv[m] % mod;
}
void NTT(int n, int *a, int opt, int mod) {
	for (int i = 0; i < n; ++i) if (i < rev[i]) swap(a[i], a[rev[i]]);
	for (int i = 1; i < n; i <<= 1) {
		int gn = Pow(G, (mod - 1) / (i << 1), mod);
		for (int j = 0; j < n; j += (i << 1)) {
			int t1, t2, g = 1;
			for (int k = 0; k < i; ++k, g = 1LL * g*gn%mod) {
				t1 = a[j + k], t2 = 1LL * g*a[j + k + i] % mod;
				a[j + k] = (t1 + t2) % mod, a[j + k + i] = (t1 - t2 + mod) % mod;
			}
		}
	}
	if (opt == -1){
		int ny = Pow(n, mod - 2, mod); reverse(a + 1, a + n);//在FFT里面,我们已经利用x在步骤3中完成了翻转。
		for (int i = 0; i < n; ++i) a[i] = 1LL * a[i] * ny%mod;
	}
}
int ans[3][N], tmp[N];
ll CRT(ll x, ll y, ll z){
	ll A = (mul(1ll * x * mod2%M, Pow(mod2%mod1, mod1 - 2, mod1), M) +
		mul(1ll * y * mod1%M, Pow(mod1%mod2, mod2 - 2, mod2), M)) % M;
	ll K = ((z - A) % mod3 + mod3) % mod3*Pow(M%mod3, mod3 - 2, mod3) % mod3;
	return ((K%mod)*(M%mod) % mod + A%mod) % mod;
}
int BB[3][N];
void work(int n, int *a, int *b){
	if (n == 1){
		b[0] = CRT(Pow(a[0], mod1 - 2, mod1),
		Pow(a[0], mod2 - 2, mod2), Pow(a[0], mod3 - 2, mod3)); return;
	}
	work(n >> 1, a, b);
	int len = 0, x = 1;
	while (x < (n << 1)) x <<= 1, ++len;
	for (int i = 1; i < x; i++) 
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (len - 1));
	for (int k = 0; k < 3; k++)
	for (int i = 0; i < n; i++)	
		ans[k][i] = b[i], ans[k][i + n] = 0;
	for (int k = 0; k < 3; k++){
		for (int i = 0; i < n; i++) tmp[i] = a[i], tmp[i + n] = 0;
		NTT(x, tmp, 1, m[k]), NTT(x, ans[k], 1, m[k]);
		for (int i = 0; i < x; i++)
			ans[k][i] = 1ll * tmp[i] * ans[k][i] % m[k];
		NTT(x, ans[k], -1, m[k]);
	}
	for (int i = 0; i < n; i++)
	ans[0][i]=ans[1][i]=ans[2][i]=(mod-CRT(ans[0][i],ans[1][i],ans[2][i])) % mod;
	ans[0][0] = ans[1][0] = ans[2][0] = (ans[0][0] + 2) % mod;

	for (int k = 0; k < 3; k++){
		for (int i = 0; i < n; i++) tmp[i] = b[i], tmp[i + n] = 0;
		NTT(x, tmp, 1, m[k]), NTT(x, ans[k], 1, m[k]);
		for (int i = 0; i < x; i++)
			ans[k][i] = 1ll * tmp[i] * ans[k][i] % m[k];
		NTT(x, ans[k], -1, m[k]);
	}
	for (int i = 0; i < n; i++)
		b[i] = CRT(ans[0][i], ans[1][i], ans[2][i]);
}
void init(){
	f[0] = inv[0] = 1;
	for (int i = 1; i < N; i++){
		f[i] = 1ll * f[i - 1] * i% mod;
		inv[i] = 1ll * inv[i - 1] * Pow(i, mod - 2, mod) % mod;
	}
	for (int i = 0; i < N - 1; i++) a[i] = inv[i + 1];
	int len = 1 << 16; work(len, a, B);
	for (int i = 0; i < len; i++) B[i] = 1ll * B[i] * f[i] % mod;
}
int main(){
	init(); B[1] =mod-B[1];//B[1]+=1; work中m[] 为分解模数
	ll n, k; cin >> n >> k;
	ll ans = Pow(k + 1, mod - 2, mod);
	ll num = 0;
	for (int i = 1; i <= k+1; i++)
		num = (num + 1ll*C(k + 1, i)*B[k+1-i]% mod*Pow(n , i, mod) % mod) % mod;
	cout << ans*num%mod << endl;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值