1. 程式人生 > 其它 >數學基礎 5

數學基礎 5

目錄

數學基礎 5

前言

因為 \(3\) 寫不出來了 \(4\) 現在還空著 但是計劃被打亂 所以來寫 \(5\) (大霧


目前還沒寫完 後面先咕著 學了之後再寫


拉格朗日插值

拉格朗日插值法

P4781 【模板】拉格朗日插值

眾所周知 給定 \(n + 1\) 個點 可以確定一個 \(n\) 次的多項式

\[f(x) = \sum_{i = 0}^{n}y_i\prod_{i \ne j}\frac{x-x_j}{x_i - x_j} \]

求其中某個點對應函式值時 將其代入即可

證明? 沒有證明(我並不會

程式碼

/*
  Time: 6.27
  Worker: Blank_space
  Source: P4781 【模板】拉格朗日插值
*/
/*--------------------------------------------*/
#include<cstdio>
#define int long long
/*--------------------------------------標頭檔案*/
const int mod = 998244353;
int n, k, X[2010], Y[2010], ans;
/*------------------------------------變數定義*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快讀*/
int power(int _a, int _b, int res = 1) {for(; _b; _a = _a * _a % mod, _b >>= 1) if(_b & 1) res = res * _a % mod; return res;}
/*----------------------------------------函式*/
signed main() {
	n = read(); k = read();
	for(int i = 1; i <= n; i++) X[i] = read(), Y[i] = read();
	for(int i = 1; i <= n; i++)
	{
		int tmp1 = Y[i], tmp2 = 1;
		for(int j = 1; j <= n; j++) if(i != j)
			tmp1 = (tmp1 * (k - X[j]) % mod + mod) % mod, tmp2 = (tmp2 * (X[i] - X[j]) % mod + mod) % mod;
		ans = (ans + tmp1 * power(tmp2, mod - 2) % mod) % mod;
	}
	printf("%lld", ans);
	return 0;
}


\(x\) 值連續的時候

\(x_i\) 換成 \(i\) 有:

\[f(k) = \sum_{i = 0}^ny_i\prod_{i \ne j}\frac{k - j}{i - j} \]

考慮優化後面那一坨東西

分子維護關於 \(k\) 的字首積和字尾積 分母直接用階乘來表示 式子為:

\[f(k) = \sum_{i = 0}^n\left(-1\right)^{n - i}y_i\frac{pre_{i - 1}\times suf_{i + 1}}{fac_i \times fac_{n - i}} \]

重心拉格朗日插值法

\[\begin{array}\\ f(x) & = & \sum_{i = 1}^ny_i\prod_{i \ne j}\frac{x - x_j}{x_i - x_j} \\ & = & \sum_{i = 1}^ny_i\frac{\prod_{j \ne i}\left(x - x_j\right)}{\prod_{i \ne j}\left(x_i - x_j\right)}\\ & = & \sum_{i = 1}^ny_i\frac{\prod_{j = 1}^n\left(x - x_j\right)}{\left(x - x_i\right)\prod_{j \ne i}\left(x_i - x_j\right) }\\ & = & \prod_{i = 1}^n\left(x - x_i\right)\sum_{i = 1}^n\frac{y_i}{\left(x - x_i\right)\prod_{i \ne j}\left(x_i - x_i\right)} \end{array} \]

\(g = \prod_{i = 1}^n\left(x - x_i\right), w(i) = \prod_{j \ne i}\left(x_i - x_j\right)\)

有:

\[f(x) = g\sum_{i = 1}^n\frac{y_i}{\left(x - x_i\right)w(i)} \]

對於每一個新增加的插值點 \(O(n)\) 的更新所有的 \(w(i)\)

程式碼

/*
  Time: 6.27
  Worker: Blank_space
  Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#define int long long
/*--------------------------------------標頭檔案*/
const int mod = 1e9 + 7;
int n, X[3010], Y[3010], W[3010], cnt;
/*------------------------------------變數定義*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快讀*/
int power(int _a, int _b) {int res = 1; for(; _b; _a = _a * _a % mod, _b >>= 1) if(_b & 1) res = res * _a % mod; return res;}
void insert(int x, int y) {
	X[++cnt] = x; Y[cnt] = y; W[cnt] = 1;
	for(int i = 1; i < cnt; i++) W[i] = W[i] * (X[i] - x) % mod, W[cnt] = W[cnt] * (x - X[i]) % mod;
}
void work(int x) {
	int g = 1, ans = 0;
	for(int i = 1; i <= cnt; i++)
		if(X[i] == x) {printf("%lld\n", Y[i]); return ;} 
		else g = g * (x - X[i]) % mod;
	for(int i = 1; i <= cnt; i++) 
	ans = (ans + Y[i] * power((x - X[i]) * W[i], mod - 2) % mod + mod) % mod;
	printf("%lld\n", ans * g % mod);
}
/*----------------------------------------函式*/
signed main() {
	n = read();
	for(int i = 1; i <= n; i++)
	{
		int opt = read(), x = read();
		if(opt == 1) insert(x, read());
		else work(x);
	}
	return 0;
}


自然數冪和

定義

\(n\) 個自然數 \(k\) 次冪的和為

\[S_k(n) = \sum_{i = 1}^ni^k \]

性質

\(S_k(n)\) 為關於 \(n\)\(k + 1\) 次的多項式

拉格朗日插值法

需要 \(k + 2\) 個點來計算 可以直接取點 \(O(k^2)\) 的計算

題目對於選擇的點沒有要求 當然可以選取連續的一段 按照上面那個直接帶進去:

\[S_k(n) = \sum_{i = 1}^{k + 2}(-1)^{k + 2 - i}S_k(i)\frac{pre_{i - 1} \times suf_{i + 1}}{fac_{i} \times fac_{k + 2 - i}} \]

複雜度: \(O(k\log k)\)

程式碼:

/*
  Time: 6.27
  Worker: Blank_space
  Source: CF622F The Sum of the k-th Powers
*/
/*--------------------------------------------*/
#include<cstdio>
#define int long long
/*--------------------------------------標頭檔案*/
const int C = 1e6 + 7;
const int mod = 1e9 + 7;
/*------------------------------------常量定義*/
int n, k, ans, tmp, pre[C], suf[C], fac[C];
/*------------------------------------變數定義*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快讀*/
int power(int _a, int _b, int res = 1) {for(; _b; _a = _a * _a % mod, _b >>= 1) if(_b & 1) res = res * _a % mod; return res;}
/*----------------------------------------函式*/
signed main() {
	n = read(); k = read(); pre[0] = suf[k + 3] = fac[0] = 1;
	for(int i = 1; i <= k + 2; i++) pre[i] = pre[i - 1] * (n - i) % mod;
	for(int i = k + 2; i >= 1; i--) suf[i] = suf[i + 1] * (n - i) % mod;
	for(int i = 1; i <= k + 2; i++) fac[i] = fac[i - 1] * i % mod;
	for(int i = 1; i <= k + 2; i++)
	{
		tmp = (tmp + power(i, k) % mod) % mod;
		int x = pre[i - 1] * suf[i + 1] % mod;
		int y = ((k - i & 1) ? -1 : 1) * fac[i - 1] * fac[k + 2 - i] % mod;
		ans = (ans + tmp * x % mod * power(y, mod - 2) % mod + mod) % mod;
	}
	printf("%lld", ans);
	return 0;
}

其他方法

然而我並不會其他方法(巨霧

題目

教科書般的褻瀆

首先 由題:

\[ans = \sum_{i = 0}^m\left(\sum_{j = 1}^{n - a_i}j^{m + 1} - \sum_{j = i + 1}^m\left(a_j - a_i \right)^{m + 1} \right) \]

然而這個式子我並沒有推出來

然後? 然後就沒有然後了 直接算就好了

程式碼

/*
  Time: 6.27
  Worker: Blank_space
  Source: P4593 [TJOI2018]教科書般的褻瀆
*/
/*--------------------------------------------*/
#include<cstdio>
#include<algorithm>
#define int long long
/*--------------------------------------標頭檔案*/
const int mod = 1e9 + 7;
/*------------------------------------常量定義*/
int T, n, m, k, a[60], ans, tmp, pre[60], suf[60], fac[60];
/*------------------------------------變數定義*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
/*----------------------------------------快讀*/
int power(int _a, int _b, int res = 1) {for(; _b; _a = _a * _a % mod, _b >>= 1) if(_b & 1) res = res * _a % mod; return res;}
int Sum(int t) {
	pre[0] = suf[k + 3] = fac[0] = 1; tmp = 0; int res = 0;
	for(int i = 1; i <= k + 2; i++) pre[i] = pre[i - 1] * (t - i) % mod;
	for(int i = k + 2; i >= 1; i--) suf[i] = suf[i + 1] * (t - i) % mod;
	for(int i = 1; i <= k + 2; i++) fac[i] = fac[i - 1] * i % mod;
	for(int i = 1; i <= k + 2; i++)
	{
		tmp = (tmp + power(i, k) % mod) % mod;
		int x = pre[i - 1] * suf[i + 1] % mod;
		int y = ((k - i & 1) ? -1 : 1) * fac[i - 1] * fac[k + 2 - i] % mod;
		res = (res + tmp * x % mod * power(y, mod - 2) % mod + mod) % mod;
	}
	return res;
}
void work() {
	n = read(); m = read(); ans = 0; k = m + 1;
	for(int i = 1; i <= m; i++) a[i] = read();
	std::sort(a + 1, a + 1 + m);
	for(int i = 0; i <= m; i++)
	{
		int sum = Sum(n - a[i]) % mod;
		for(int j = i + 1; j <= m; j++) sum = ((sum - power(a[j] - a[i], k) % mod) % mod + mod) % mod;
		ans = (ans + sum) % mod;
	}
	printf("%lld\n", (ans + mod) % mod);
}
/*----------------------------------------函式*/
signed main() {
	T = read(); while(T--) work();
	return 0;
}


待填