【UOJ #269. 【清華集訓2016】如何優雅地求和】【生成函數+下降冪多項式】

題意

給出n,m,xn,m,x和一個mm次多項式f(x)f(x)x=0,1,,mx=0,1,\cdots,m處的點值,計算k=0nf(k)(nk)xk(1x)xk\sum_{k=0}^nf(k)\binom{n}{k}x^k(1-x)^{x-k}

n109,m2104n\le 10^9,m\le 2*10^4

分析

若我們已經把f(x)f(x)轉成了下降冪多項式的形式f(x)=i=0mfixif(x)=\sum_{i=0}^mf_ix^{\underline{i}}

那麼ans=k=0nf(k)(nk)xk(1x)nk=k=0ni=0mfiki(nk)xk(1x)xk=i=0mfik=0nki(nk)xk(1x)nk=i=0nfik=0nki(niki)nikixk(1x)k=i=0nfinik=0n(niki)xk(1x)nk=i=0nfinixik=0ni(nik)xk(1x)nik=i=0nfinixi\begin{aligned} ans&=\sum_{k=0}^nf(k)\binom{n}{k}x^k(1-x)^{n-k}\\ &=\sum_{k=0}^n\sum_{i=0}^mf_ik^{\underline{i}}\binom{n}{k}x^k(1-x)^{x-k}\\ &=\sum_{i=0}^mf_i\sum_{k=0}^nk^{\underline{i}}\binom{n}{k}x^k(1-x)^{n-k}\\ &=\sum_{i=0}^nf_i\sum_{k=0}^nk^{\underline{i}}\binom{n-i}{k-i}\frac{n^{\underline{i}}}{k^{\underline{i}}}x^k(1-x)^k\\ &=\sum_{i=0}^nf_in^{\underline{i}}\sum_{k=0}^n\binom{n-i}{k-i}x^k(1-x)^{n-k}\\ &=\sum_{i=0}^nf_in^{\underline{i}}x^i\sum_{k=0}^{n-i}\binom{n-i}{k}x^k(1-x)^{n-i-k}\\ &=\sum_{i=0}^nf_in^{\underline{i}}x^i \end{aligned}

問題轉化爲如何將點值轉成下降冪。考慮f(x)f(x)點值的EGFn=0mf(n)n!xn=n=0mxnn!i=0nfini=i=0nfinimxn(ni)!\sum_{n=0}^m\frac{f(n)}{n!}x^n=\sum_{n=0}^m\frac{x^n}{n!}\sum_{i=0}^nf_in^{\underline{i}}=\sum_{i=0}^nf_i\sum_{n-i}^m\frac{x^n}{(n-i)!}

=i=0nfixin=0mixnn!=(i=0nfixi)ex(modxn+1)=\sum_{i=0}^nf_ix^i\sum_{n=0}^{m-i}\frac{x^n}{n!}=(\sum_{i=0}^nf_ix^i)e^x\pmod {x^{n+1}}

因此只要把點值的EGF乘上exe^{-x},就可以得到下降冪的OGF了。

時間複雜度O(mlogm)O(m\log m)

代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 100005;
const int MOD = 998244353;

int n, m, x, jc[N], ny[N], rev[N], L, a[N], b[N];

void pre()
{
	jc[0] = jc[1] = ny[0] = ny[1] = 1;
	for (int i = 2; i <= m; i++) jc[i] = (LL)jc[i - 1] * i % MOD, ny[i] = (LL)(MOD - MOD / i) * ny[MOD % i] % MOD;
	for (int i = 2; i <= m; i++) ny[i] = (LL)ny[i - 1] * ny[i] % MOD;
	int lg = 0;
	for (L = 1; L <= m * 2; L <<= 1, lg++);
	for (int i = 0; i < L; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1));
}

int ksm(int x, int y)
{
	int ans = 1;
	while (y)
	{
		if (y & 1) ans = (LL)ans * x % MOD;
		x = (LL)x * x % MOD; y >>= 1;
	}
	return ans;
}

void NTT(int * a, int f)
{
	for (int i = 0; i < L; i++) if (i < rev[i]) swap(a[i], a[rev[i]]);
	for (int i = 1; i < L; i <<= 1)
	{
		int wn = ksm(3, f == 1 ? (MOD - 1) / i / 2 : MOD - 1 - (MOD - 1) / i / 2);
		for (int j = 0; j < L; j += (i << 1))
		{
			int w = 1;
			for (int k = 0; k < i; k++)
			{
				int u = a[j + k], v = (LL)a[j + k + i] * w % MOD;
				a[j + k] = (u + v) % MOD; a[j + k + i] = (u + MOD - v) % MOD;
				w = (LL)w * wn % MOD;
			}
		}
	}
	if (f == -1) for (int i = 0, inv = ksm(L, MOD - 2); i < L; i++) a[i] = (LL)a[i] * inv % MOD;
}

int main()
{
	scanf("%d%d%d", &n, &m, &x);
	for (int i = 0; i <= m; i++) scanf("%d", &a[i]);
	pre();
	for (int i = 0; i <= m; i++) a[i] = (LL)a[i] * ny[i] % MOD, b[i] = (i & 1) ? MOD - ny[i] : ny[i];
	NTT(a, 1); NTT(b, 1);
	for (int i = 0; i < L; i++) a[i] = (LL)a[i] * b[i] % MOD;
	NTT(a, -1);
	int ans = 0, w = 1;
	for (int i = 0; i <= m; i++)
	{
		(ans += (LL)a[i] * w % MOD) %= MOD;
		w = (LL)w * (n - i) % MOD * x % MOD;
	}
	printf("%d\n", ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章