bzoj #3453. tyvj 1858 XLkxc(拉格朗日插值模板,多项式之和)

在这里插入图片描述


题意:求 ∑ i = 0 n ∑ j = 1 a + d ∗ i ∑ x = 1 j x k \displaystyle\sum_{i = 0}^n\sum_{j = 1}^{a +d*i}\sum_{x = 1}^jx^k i=0nj=1a+dix=1jxk

根据过去的经验,可以得知 ∑ i = 1 n x k \displaystyle\sum_{i = 1}^nx^k i=1nxk 是一个以 n n n 为自变量的 k + 1 k + 1 k+1 次多项式,可以得到推广结论: k k k 次多项式之和得到的是 k + 1 k + 1 k+1 次多项式。

那么就可以得知题意所求式子是一个 k + 3 k + 3 k+3 次多项式,可以通过拉格朗日插值来求解。

从内层求和到外层求和,依次进行插值,求出 m x mx mx 个点的点值( m x ≥ k + 1 mx \geq k + 1 mxk+1),对于中间某一层求和进行插值时,其 y y y 的计算依赖于上一层插值得到的多项式。由于 x x x 由自己选择可以连续,因此最高在 k 2 k^2 k2 时间内完成一层求和的插值。


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
const int mod = 1234567891;
typedef long long ll;
int mx;
inline ll add(ll x, ll y) {
  	x += y;
  	if (x >= mod) x -= mod;
  	return x;
}

inline ll sub(ll x, ll y) {
	x -= y;
	if (x < 0) x += mod;
	return x;
}

inline ll mul(ll x, ll y) {
  	return x * y % mod;
}
ll fpow(ll a,ll b) {
	ll r = 1;
	while(b) {
		if (b & 1) r = mul(r,a);
		b >>= 1;
		a = mul(a,a);
	}
	return r;
}
ll g[maxn],f[maxn],k,a,n,d,sz;
ll fac[maxn],ifac[maxn];
ll cal(ll g[maxn],ll x) {			//拉格朗日插值计算多项式
	if (x <= mx) return g[x];
	ll tmp = 1,inv,ans = 0;
	for (int i = 1; i <= mx; i++)
		tmp = mul(tmp,x - i);
	for (int i = 1; i <= mx; i++) {
		ll res = 1, inv = fpow(x - i,mod - 2);
		res = mul(res,g[i]);
		res = mul(res,ifac[i - 1]);
		res = mul(res,ifac[mx - i]);
		res = mul(res,inv);
		res = mul(res,tmp);
		if ((mx - i) & 1) res = mul(res,-1);
		if (res < 0) res += mod;
		ans = add(ans,res);
	}
	return ans;
}
int main() {
	fac[0] = 1;
	for (int i = 1; i <= 1000; i++)
		fac[i] = mul(fac[i - 1],i);
	ifac[1000] = fpow(fac[1000],mod - 2);
	for (int i = 1000 - 1; i >= 0; i--)
		ifac[i] = mul(ifac[i + 1],i + 1);
	scanf("%d",&sz);
	while (sz--) {
		scanf("%lld%lld%lld%lld",&k,&a,&n,&d);
		mx = k + 5;
		g[0] = 0;
		for (int i = 1; i <= mx; i++) {
			g[i] = g[i - 1];
			for (int j = 1; j <= i; j++) 
				g[i] = add(g[i],fpow(j,k));
		}
		f[0] = cal(g,a);
		for (int i = 1; i <= mx; i++) {
			f[i] = f[i - 1];
			ll t = 1ll * a + d * i;
			if (t <= mx) f[i] = add(f[i],g[t]);
			else f[i] = add(f[i],cal(g,t % mod));
		}
		printf("%lld\n",cal(f,n));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值