bzoj 5339 [TJOI2018]教科书般的亵渎 拉格朗日插值

题面

题目传送门

解法

可以发现,题目可以转化成求若干个形如\(\sum_{i=1}^ni^k\)的东西

这个东西可以拉格朗日插值

大概讲一下拉格朗日插值是个什么东西:

显然,\(\sum_{i=1}^ni^k\)可以用一个\(k+1\)次多项式表示出来

那么,将\(x_i=1,2,…k+1\)代入,求得\(y_i\)

表达式即可表示为$$y=\sum_{i=1}^{k+1}y_i\frac{(x-x_1)(x-x_2)…(x-x_{k+1})}{(x_i-x_1)(x_i-x_2)…(x_i-x_{k+1})}$

另外,每一项分子分母上都不会出现\(x-x_i\)\(x_i-x_i\)的东西

\(x_i\)代入,显然是正确的

时间复杂度:\(O(TM^3)\)

代码

#include <bits/stdc++.h>
#define Mod 1000000007
#define LL long long
#define N 100
using namespace std;
template <typename node> void read(node &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int x[N], y[N];
LL a[N];
int Pow(int x, int y) {
	int ret = 1;
	while (y) {
		if (y & 1) ret = 1ll * ret * x % Mod;
		y >>= 1, x = 1ll * x * x % Mod;
	}
	return ret;
}
int solve(int n, int k) {
	int ret = 0;
	for (int i = 1; i <= k + 2; i++) {
		int tx = y[i], ty = 1;
		for (int j = 1; j <= k + 2; j++) {
			if (i == j) continue;
			tx = 1ll * tx * (n - x[j]) % Mod;
			ty = 1ll * ty * (x[i] - x[j]) % Mod;
		}
		tx = 1ll * tx * Pow(ty, Mod - 2) % Mod;
		ret = ((LL)ret + tx + Mod) % Mod;
	}
	return ret;
}
int main() {
	int T; read(T);
	while (T--) {
		LL n, m; read(n), read(m); a[0] = 0;
		for (int i = 1; i <= m; i++) read(a[i]);
		int k = m + 1; sort(a + 1, a + m + 1);
		for (int i = 1; i <= k + 2; i++)
			x[i] = i, y[i] = ((LL)y[i - 1] + Pow(i, k)) % Mod;
		int ans = 0;
		for (int i = 0; i <= m; i++) {
			LL l = n - a[i];
			ans = ((LL)ans + solve(l, k)) % Mod;
			for (int j = i + 1; j <= m; j++)
				ans = (ans - Pow(a[j] - a[i], k) + Mod) % Mod;
		}
		cout << ans << "\n";
	}
	return 0;
}

posted @ 2018-08-14 22:27  谜のNOIP  阅读(103)  评论(0编辑  收藏  举报