【ybt金牌导航8-3-2】数列求值

数列求值

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

题目大意

给出一个不超过 m-1 次的函数,给出 x 为 0~m-1 时 y 的值,然后求 x 为 k 时 y 的值。

思路

这一题看到函数自然会想到拉格朗日插值法。

但是你会看到它的范围很大, n 2 n^2 n2 是过不了的。

但是你会发现它有一个特殊的地方:给出的点是连续的,而且 x i = i x_i=i xi=i

那你考虑能不能化简式子。
先把式子拿出来:
f ( k ) = ∑ i = 1 n ( y i ∏ i ≠ j k − x j x i − x j ) f(k)=\sum\limits_{i=1}^{n}(y_i\prod\limits_{i\neq j}\dfrac{k-x_j}{x_i-x_j}) f(k)=i=1n(yii=jxixjkxj)
然后 x i = i x_i=i xi=i
f ( k ) = ∑ i = 1 n ( y i ∏ i ≠ j k − j i − j ) f(k)=\sum\limits_{i=1}^{n}(y_i\prod\limits_{i\neq j}\dfrac{k-j}{i-j}) f(k)=i=1n(yii=jijkj)
那累乘的不等号条件很烦,我们就把它分开乘两个累乘:
f ( k ) = ∑ i = 1 n ( y i ∏ 1 ≤ j < i k − j i − j ∏ i < j ≤ n k − j i − j ) f(k)=\sum\limits_{i=1}^{n}(y_i\prod\limits_{1\leq j<i}\dfrac{k-j}{i-j}\prod\limits_{i<j\leq n}\dfrac{k-j}{i-j}) f(k)=i=1n(yi1j<iijkji<jnijkj)
那这里我们就发现它分子的部分分别是可以用前缀积和后缀积解决。

然后再看分母,你会发现它是两个阶乘。

那你就预处理这三个东西,就可以用了。

在处理阶乘的时候,我们可以预先把它的逆元算出来。
然后不要一个一个都转逆元,会 T,你可以求出最大的那个的逆元,然后每次再乘回去,就可以得到较小的阶乘的逆元了。

还有。
不要以为我的题意写错了。
给出的就是从 0 ∼ m − 1 0\sim m-1 0m1 的,题目是有问题的,连样例都错了。
正确的样例输出的应该是 9 9 9,而不是 7 7 7

然后我这里因为是后面才发现,就把它改成了从 0 0 0 开始记录数组,运算什么的。
上面的公式的循环范围也要小改一下,这里就不再写一次了。
(如果不知道怎么改可以看代码)

代码

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

using namespace std;

ll m, k, a[1000001];
ll y[1000001];
ll q[1000001], b[1000001];
ll jc[1000001], ans, now;

ll ksm(ll x, ll y) {
    
    //快速幂求逆元
	if (x < 0) x = (x % mo + mo) % mo;
	
	ll re = 1;
	while (y) {
    
    
		if (y & 1) re = (re * x) % mo;
		x = (x * x) % mo;
		y >>= 1;
	}
	
	return re;
}

int main() {
    
    
	scanf("%lld %lld", &m, &k);
	m--;
	
	q[0] = k - 0;
	scanf("%d", &y[0]);
	for (ll i = 1; i <= m; i++) {
    
    
		scanf("%lld", &y[i]);
		
		q[i] = q[i - 1] * ((k - i + mo) % mo) % mo;//处理分子的前缀积
	}
	
	b[m + 1] = 1ll;
	for (ll i = m; i >= 0; i--) {
    
    //处理分子的后缀积
		b[i] = b[i + 1] * ((k - i + mo) % mo) % mo;
	}
	
	jc[0] = 1ll;//求分母的阶乘
	for (ll i = 1; i <= m; i++)
		jc[i] = jc[i - 1] * i % mo;
	jc[m] = ksm(jc[m], mo - 2);//现在就把分母的阶乘的逆元弄好
	for (ll i = m - 1; i >= 0; i--)
		jc[i] = (jc[i + 1] * (i + 1)) % mo;//不要每个都求逆元,会T
	
	for (ll i = 0; i <= m; i++) {
    
    
		now = y[i];//按照公式乘
		now = (now * (((i - 1 < 0) ? 1 : q[i - 1]) * b[i + 1] % mo)) % mo;
		now = (now * (jc[i] * jc[m - i] % mo)) % mo;
		now = (now * ((m - i) & 1 ? -1 : 1)) % mo;
		if (now < 0) now += mo;
		
		ans = (ans + now) % mo;
	}
	
	printf("%lld", ans);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/114123945
今日推荐