【ybt金牌导航8-3-5】多重前缀和(拉格朗日插值)

多重前缀和

题目链接:ybt金牌导航8-3-5

题目大意

求这个式子的值。
sum i=0~n sum j=1~a+id sum l=1~j l^k
其中 n,a,d,k 都给出。

思路

重新写式子:
\(\sum\limits_{i=0}^n\sum\limits_{j=1}^{a+i*d}\sum\limits_{l=1}^jl^k\)

其实看到 \(l^k\)\(k\) 的小范围我们不难你想到用拉格朗日插值来搞这个 \(k\) 项的多项式。
然后你发现它前面还有两个前缀和。

那其实 \(\sum\limits_{l=1}^jl^k\) 这个是 \(k+1\) 项的,或者你通过证明 \(l^k\)\(k\) 项的多项式也可以看出,每次前缀和,它的项数就 \(+1\),那这道题的就是 \(k+3\) 项。

那你就一层一层的求下去即可。
表示出 \(l^k\),然后拉格朗日插值可以求出 \(\sum\limits_{l=1}^jl^k\) 的,然后表示出之后又可以拉格朗日插值求 \(\sum\limits_{j=1}^{a+i*d}\sum\limits_{l=1}^j l^k\) 的,然后再表示出来再求就可以得到 \(\sum\limits_{i=0}^n\sum\limits_{j=1}^{a+i*d}\sum\limits_{l=1}^jl^k\) 了。

代码

#include<cstdio>
#define ll long long
#define mo 1234567891

using namespace std;

int T;
ll k, a, n, d, y[131], f[131], g[131];
ll pre[131], suf[131], jc[131];

ll ksm(ll x, ll y) {
	ll re = 1;
	while (y) {
		if (y & 1) re = re * x % mo;
		x = x * x % mo;
		y >>= 1;
	}
	return re;
}

ll query(ll *f, ll k, ll n) {//拉格朗日插值
	ll re = 0;
	pre[0] = 1;
	for (int i = 1; i <= k; i++) pre[i] = pre[i - 1] * ((n - i + mo) % mo) % mo;//这里 n-i 要单独取模了再运算,因为你的 n 在求 y 数组的时候是 long long 级别的
	suf[k + 1] = 1;
	for (int i = k; i >= 1; i--) suf[i] = suf[i + 1] * ((n - i + mo) % mo) % mo;
	
	for (int i = 1; i <= k; i++) {
		re = (re + f[i] * pre[i - 1] % mo * suf[i + 1] % mo * ksm(jc[i - 1] * jc[k - i] % mo * (((k - i) & 1) ? mo - 1 : 1) % mo, mo - 2) % mo) % mo;
	}
	return re;
}

int main() {
	jc[0] = 1;
	for (int i = 1; i <= 130; i++) jc[i] = jc[i - 1] * i % mo;
	
	scanf("%d", &T);
	while (T--) {
		scanf("%lld %lld %lld %lld", &k, &a, &n, &d);
		
		for (int i = 1; i <= k + 2; i++)
			f[i] = (f[i - 1] + ksm(i, k)) % mo;
		for (int i = 1; i <= k + 3; i++)
			g[i] = (g[i - 1] + query(f, k + 2, i)) % mo;
		y[0] = query(g, k + 3, a);//记得这个前缀是从 0 开始
		for (int i = 1; i <= k + 4; i++)
			y[i] = (y[i - 1] + query(g, k + 3, a + d * i)) % mo;
		
		printf("%lld\n", query(y, k + 4, n));
	}
	
	return 0;
}
posted @ 2021-12-09 09:56  あおいSakura  阅读(59)  评论(0编辑  收藏  举报