AzusidNya人傻常數大

AzusidNya發表於2024-06-07

AzusidNya 17分鐘前:

多項式快速冪 \(n\log n\)\(1e5\) 跑了 \(4\) 秒,樂

  • 刪除

P5488 差分與字首和

給定一個長為 \(n\) 的序列 \(a\),求出其 \(k\) 階差分或字首和。
結果的每一項都需要對 \(1004535809\) 取模。

\(1 \le n \le 10^5\)
\(0 \le a_i \le 10^9\)
\(1\le k \le 10^{2333}, k \not \equiv 0 \pmod{1004535809}\)

看過《具體數學》的話,很容易看出字首和就是其生成函式卷積上 \((1 - x) ^ {-1}\),差分就是捲上 \((1 - x)\)

當然這裡也推一下吧。

\[\sum_{i} \left(\sum _{j = 0} ^{i}a_j\right)x^i = \sum _i \sum _{u + v = i} a_ux^u \times x^v \]

定義 \(A = \sum _{i} a_ix^i\)\(B = \sum _i x^i\)

\[\sum _i \sum _{u + v = i} a_ux^u \times x^v = A * B \]

\[\sum _i x^i = (1 - x) ^{-1} \]

所以很進行一次字首和就是捲上 \((1 - x) ^ {-1}\),而差分和字首和是逆運算,所以只用捲上 \((1 - x)\)​ 就行了。

\(n\) 次字首和就是捲上 \((1 - x) ^ {-k}\),差分就是捲上 \((1 - x)^k\)

之前做過多項式快速冪,知道這裡這麼大的 \(k\) 是可以直接對模數取模的。

多項式快速冪和多項式求逆拍上去,做完了(誤

好吧 AzusidNya 推到這一步這麼做了,然後就有了上面第一句話。

雖然時間複雜度是 \(O(n \log n)\)​ 的,但是常數巨大。考慮最佳化,能卡常過去的可以無視下面的內容。

我們感覺 \((1 - x)^k\) 是很好看的,考慮不快速冪求出這個多項式的係數。

透過二項式定理把它展開,得到:

\[(1 - x) ^k = \sum _i (-1) ^i \binom ki x^i \]

這個式子可以遞推求出來。

具體來說:

\[\binom ki = \frac {k!}{i!(k - i)!} = \frac {k - i + 1}{i} \frac {k!}{(i - 1)!(k - i + 1)!} = \frac {k - i + 1}{i}\binom{k}{i - 1} \]

然後我們可以把係數搞出來。

接下來求字首和完全可以套一個多項式求逆。這樣已經不會 TLE 了。

但是其實字首和也能一樣地用遞推求出所有係數。

找回上面的式子:

\[(1 - x) ^k = \sum _i (-1) ^i \binom ki x^i \]

這裡用下降冪表示二項式係數:

\[\binom ki = \frac{k^{\underline i}}{i!} \]

\(-k\) 代進去。

\[\begin {aligned} (1 - x) ^ {-k} &= \sum _i (-1) ^i \binom {-k}i x^i \\ &= \sum _i (-1) ^i \frac {(-k) ^ {\underline i}}{i!} x^i \\ &= \sum _i (-1) ^ {2i} \frac {k + i - 1 ^ {\underline i}}{i!} x^i \\ &= \sum_i \binom {k + i - 1}{i} x^i \end {aligned} \]

這玩意也是可以遞推的,和上面的推導方法差不多,這裡懶得再推一遍了。

這兩個多項式的係數遞推完後直接卷積就行了,問題變成了 【模板】多項式乘法。

程式碼刪除了 namespace Poly 部分,因為 \(1000\) 個人有 \(1000\) 個多項式板子。

#include<iostream>
#include<fstream>
#include<algorithm>
#include<vector>
#include<string>
#define int long long
using namespace std;

namespace azus{
	using namespace Poly;
	int n, opt;
	int k;
	string sk;
	poly a;
	int main(){
		cin >> n >> sk >> opt;
		for(int i = 0; i < sk.size(); i ++){
			k = 1ll * k * 10 % P;
			k += sk[i] - '0';
			k %= P;
		}
//		cout << k << "\n";
		for(int i = 0, x; i < n; i ++){
			cin >> x;
			a.push_back(x);
		}
		poly d;
		d.resize(n);
		d[0] = 1;
		if(opt == 0)
		for(int i = 1; i < n; i ++)
			d[i] = (d[i - 1] * ((P + k + i - 1) % P) % P) * Ksm(i, P - 2) % P;
		else{
			for(int i = 1; i < n; i ++)
				d[i] = ((P - d[i - 1]) * ((P + k - i + 1) % P) % P) * Ksm(i, P - 2) % P;
		}
//		polyOutput(d);
		a = convolution(a, d);
		a.resize(n);
		polyOutput(a);
		return 0;
	}
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int T = 1;
	while(T --) azus::main();
	return 0;
}

相關文章