LOJ#2320「清华集训 2017」生成树计数

6 篇文章 0 订阅

Address

Algorithm 1

  • 先特判 n = 1 n = 1 n=1
  • 注意到一棵树 T T T 的价值只与每个点的度数有关,如果我们已经确定了每个点的度数 d i d_i di,由 prufer序列 可知,对答案产生的贡献为(任意两个连通块 i , j i,j i,j 之间的连边方案有 a i a j a_ia_j aiaj 种):
    ( n − 2 ) ! ∏ i = 1 n ( d i − 1 ) ! ( ∏ i = 1 n d i m ) ( ∑ i = 1 n d i m ) ( ∏ i = 1 n a i d i ) ( n − 2 ) ! ( ∑ i = 1 n d i m ) ( ∏ i = 1 n d i m a i d i ( d i − 1 ) ! ) \begin{aligned} \frac{(n - 2)!}{\prod \limits_{i = 1}^{n}(d_i - 1)!}\left(\prod \limits_{i = 1}^{n}d_i ^m\right)\left( \sum \limits_{i = 1}^{n}d_i^m\right) \left(\prod \limits_{i = 1}^{n}a_i^{d_i}\right)\\ (n - 2)! \left(\sum \limits_{i = 1}^{n}d_i^m\right)\left(\prod \limits_{i = 1}^{n}\frac{d_i^ma_i^{d_i}}{(d_i - 1)!}\right) \end{aligned} i=1n(di1)!(n2)!(i=1ndim)(i=1ndim)(i=1naidi)(n2)!(i=1ndim)(i=1n(di1)!dimaidi)
  • 前面的和式很不好处理,考虑和式的实际意义。
  • 首先枚举某一个点 i i i 的度数 d i d_i di,乘积的部分可以看做给剩余的点分配度数,可以用一个背包 DP \text{DP} DP 解决,然后将这一部分的贡献乘上 d i m d_i^m dim 加到答案中。
  • 预处理出前后缀的 DP \text{DP} DP 数组,在枚举到每个点的时候暴力合并即可,时间复杂度 O ( n 3 ) \mathcal O(n^3) O(n3),期望得分 20pts \text{20pts} 20pts

Algorithm 2

  • 不难发现,转移是卷积的形式,NTT 优化即可。
  • 具体地,设生成函数
    F ( x ) = ∑ i = 0 n − 2 ( i + 1 ) m i ! x i G ( x ) = ∑ i = 0 n − 2 ( i + 1 ) 2 m i ! x i \begin{aligned} F(x) = \sum \limits_{i = 0}^{n - 2}\frac{(i + 1)^m}{i!}x^i \\ G(x) = \sum \limits_{i = 0}^{n - 2}\frac{(i + 1)^{2m}}{i!}x^i \end{aligned} F(x)=i=0n2i!(i+1)mxiG(x)=i=0n2i!(i+1)2mxi
  • 则答案可以表示为:
    ( ( n − 2 ) ! ∏ i = 1 n a i ) [ x n − 2 ] ( ∑ i = 1 n G ( a i x ) ∏ j ≠ i j = 1 j = n F ( a j x ) ) \begin{aligned} \left((n - 2)! \prod \limits_{i = 1}^{n} a_i \right) [x^{n - 2}]\left(\sum \limits_{i = 1}^{n}G(a_ix) \prod \limits_{^{j = 1}_{j \neq i}}^{j = n} F(a_jx)\right)\\ \end{aligned} ((n2)!i=1nai)[xn2]i=1nG(aix)j=ij=1j=nF(ajx)
  • 时间复杂度 O ( n 2 log ⁡ n ) \mathcal O(n^2 \log n) O(n2logn),期望得分 40pts \text{40pts} 40pts
  • 常数较大,需要尽量减小 NTT 的次数。

Algorithm 3

  • 针对所有 a i a_i ai 相同的数据。
  • 显然此时每个点产生的贡献相同,答案可以简化为:
    ( n ( n − 2 ) ! ∏ i = 1 n a i ) [ x n − 2 ] ( G ( a 1 x ) F ( a 1 x ) n − 1 ) \begin{aligned} \left(n(n - 2)! \prod \limits_{i = 1}^{n} a_i \right) [x^{n - 2}]\left(G(a_1x) F(a_1x)^{n - 1}\right)\\ \end{aligned} (n(n2)!i=1nai)[xn2](G(a1x)F(a1x)n1)
  • 多项式快速幂即可,时间复杂度 O ( n log ⁡ 2 n ) \mathcal O(n \log^2 n) O(nlog2n),结合 Algorithm 2 \text{Algorithm 2} Algorithm 2 期望得分 60pts \text{60pts} 60pts

Code 1

  • 60pts \text{60pts} 60pts
#include<bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch;
	while (ch = getchar(), !isdigit(ch));
	res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
}

const int N = 3e4 + 5;
const int M = 12e4 + 5;
const int mod = 998244353;
const int inv3 = (mod + 1) / 3;
int n, m, ans;
int a[N], ex_m[N], fra[N], inv[N];
int rev[M], tw[M];

inline int quick_pow(int x, int k)
{
	int res = 1;
	while (k)
	{
		if (k & 1)
			res = 1ll * res * x % mod;
		x = 1ll * x * x % mod;
		k >>= 1;
	}
	return res;
}

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

inline void NTT(int *f, int fm, int opt)
{
	int g = opt == 1 ? 3 : inv3;
	for (int i = 0; i < fm; ++i)
		if (i < rev[i])
			std::swap(f[i], f[rev[i]]);
	for (int k = 1; k < fm; k <<= 1)
	{
		int w = quick_pow(g, (mod - 1) / (k << 1));
		tw[0] = 1;
		for (int j = 1; j < k; ++j)
			tw[j] = 1ll * tw[j - 1] * w % mod;
		for (int i = 0; i < fm; i += k << 1)
			for (int j = 0, *f1 = f + i, *f2 = f + i + k; j < k; ++j, ++f1, ++f2)
			{
				int u = *f1,
					v = 1ll * tw[j] * (*f2) % mod;
				*f1 = *f2 = u;
				add(*f1, v);
				dec(*f2, v);
			}
	}

	if (opt == -1)
	{
		for (int i = 0, inv = quick_pow(fm, mod - 2); i < fm; ++i)
			f[i] = 1ll * f[i] * inv % mod;
	}
}

const int N1 = 3e3 + 5;
const int M1 = 12e3 + 5;
int val[N1][N1], pre[N1][N1], suf[N1][N1];
int val_val[N1][M1], pre_val[N1][M1], suf_val[N1][M1];
int f[M], g[M], h[M], tmp_g[M];

inline void poly_mul(int *a, int *b, int am, int fm)
{
	NTT(a, fm, 1);
	NTT(b, fm, 1);
	for (int i = 0; i < fm; ++i)
		a[i] = 1ll * a[i] * b[i] % mod;
	NTT(a, fm, -1);
	for (int i = am + 1; i < fm; ++i)
		a[i] = 0;
	for (int i = 0; i < fm; ++i)
		b[i] = 0;
}	

inline void poly_self(int *a, int am, int fm)
{
	NTT(a, fm, 1);
	for (int i = 0; i < fm; ++i)
		a[i] = 1ll * a[i] * a[i] % mod;
	NTT(a, fm, -1);
	for (int i = am + 1; i < fm; ++i)
		a[i] = 0;
}

inline void write(int *a, int *b, int am, int fm)
{
	for (int i = 0; i < fm; ++i)
		f[i] = 0;
	for (int i = 0; i <= am; ++i)
		f[i] = a[i];
	NTT(f, fm, 1);
	for (int i = 0; i < fm; ++i)
		b[i] = f[i];
	for (int i = 0; i < fm; ++i)
		f[i] = 0;		
}

namespace task1
{
	inline void work()
	{
		pre[0][0] = suf[n + 1][0] = 1;
		for (int i = 1; i <= n; ++i)
		{
			int res = 1;
			for (int j = 1; j < n; ++j)
			{
				res = 1ll * res * a[i] % mod;
				val[i][j] = 1ll * ex_m[j] * inv[j - 1] % mod * res % mod;
			}
		}
		int tmp = n - 1 << 1;
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j < n; ++j)
				for (int k = j; k <= tmp; ++k)
					pre[i][k] = (1ll * pre[i - 1][k - j] * val[i][j] + pre[i][k]) % mod;
		for (int i = n; i >= 1; --i)
			for (int j = 1; j < n; ++j)
				for (int k = j; k <= tmp; ++k)
					suf[i][k] = (1ll * suf[i + 1][k - j] * val[i][j] + suf[i][k]) % mod;
		for (int i = 1; i <= n; ++i)
		{
			for (int k = 0; k <= tmp; ++k)
				g[k] = 0;
			for (int j = 0; j <= tmp; ++j)
				for (int k = 0; j + k <= tmp; ++k)
					g[j + k] = (1ll * pre[i - 1][j] * suf[i + 1][k] + g[j + k]) % mod;
			for (int j = 1; j < n; ++j)
				ans = (1ll * ex_m[j] * val[i][j] % mod * g[tmp - j] + ans) % mod;	 
		}
		printf("%d\n", 1ll * fra[n - 2] * ans % mod);
	}
}

namespace task2
{
	inline void work()
	{
		int tot = n - 2 << 1, tmp = n - 2, fm, k = -1;
		for (fm = 1; fm <= tot; fm <<= 1, ++k);
		for (int i = 1; i < fm; ++i)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << k);

		pre[0][0] = suf[n + 1][0] = 1;
		write(pre[0], pre_val[0], tmp, fm);
		write(suf[n + 1], suf_val[n + 1], tmp, fm);

		for (int i = 1; i <= n; ++i)
		{
			int res = 1;
			for (int j = 1; j < n; ++j)
			{
				res = 1ll * res * a[i] % mod;
				val[i][j - 1] = 1ll * ex_m[j] * inv[j - 1] % mod * res % mod;
			}
			write(val[i], val_val[i], tmp, fm);
		}
		for (int i = 1; i <= n; ++i)
		{
			for (int j = 0; j < fm; ++j)
				f[j] = 1ll * pre_val[i - 1][j] * val_val[i][j] % mod;
			NTT(f, fm, -1);
			for (int j = 0; j <= tmp; ++j)
				pre[i][j] = f[j];
			write(pre[i], pre_val[i], tmp, fm);
		}
		for (int i = n; i >= 1; --i)
		{
			for (int j = 0; j < fm; ++j)
				f[j] = 1ll * suf_val[i + 1][j] * val_val[i][j] % mod;
			NTT(f, fm, -1);
			for (int j = 0; j <= tmp; ++j)
				suf[i][j] = f[j];
			write(suf[i], suf_val[i], tmp, fm);
		}
		for (int i = 1; i <= n; ++i)
		{
			for (int j = 0; j < fm; ++j)
				f[j] = 1ll * pre_val[i - 1][j] * suf_val[i + 1][j] % mod;
			NTT(f, fm, -1);
			for (int j = 1; j < n; ++j)
				ans = (1ll * ex_m[j] * val[i][j - 1] % mod * f[tmp - j + 1] + ans) % mod;	 
		}
		printf("%d\n", 1ll * fra[n - 2] * ans % mod);
	}	
}

namespace task3
{
	inline void work()
	{
		int tot = n - 2 << 1, tmp = n - 2, fm, k = -1;
		for (fm = 1; fm <= tot; fm <<= 1, ++k);
		for (int i = 1; i < fm; ++i)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << k);
		
		int res = 1;
		for (int i = 0; i <= tmp; ++i)
		{
			res = 1ll * res * a[1] % mod;
			g[i] = 1ll * ex_m[i + 1] * inv[i] % mod * res % mod;
			f[i] = 1ll * g[i] * ex_m[i + 1] % mod;
		}

		h[0] = 1;
		k = n - 1;
		while (k)
		{
			if (k & 1)
			{
				for (int i = 0; i <= tmp; ++i)
					tmp_g[i] = g[i];
				poly_mul(h, tmp_g, tmp, fm);
			}
			poly_self(g, tmp, fm);
			k >>= 1;
		}

		poly_mul(f, h, tmp, fm);
		printf("%d\n", 1ll * n * fra[n - 2] % mod * f[n - 2] % mod);
	}
}

int main()
{
	read(n); read(m);
	if (n == 1)
		return puts(m == 0 ? "1" : "0"), 0;
	
	for (int i = 1; i <= n; ++i)
		read(a[i]);
	
	for (int i = 1; i <= n; ++i)
		 ex_m[i] = quick_pow(i, m);
	fra[0] = 1;
	for (int i = 1; i <= n; ++i)
		fra[i] = 1ll * fra[i - 1] * i % mod;
	inv[n] = quick_pow(fra[n], mod - 2);
	for (int i = n; i >= 1; --i)
		inv[i - 1] = 1ll * inv[i] * i % mod;

	if (n <= 500)
		task1::work();
	else if (n <= 3000)
		task2::work();
	else 
		task3::work();
}	

Algorithm 4

  • 针对 m = 1 m = 1 m=1 的数据。
  • 此时 ∑ i = 1 n d i m = 2 n − 2 \sum \limits_{i = 1}^{n}d_i^m = 2n - 2 i=1ndim=2n2,答案可以简化为:
    ( ( n − 2 ) ! ( 2 n − 2 ) ∏ i = 1 n a i ) [ x n − 2 ] ∏ i = 1 n F ( a i x ) \begin{aligned} \left((n - 2)!(2n - 2)\prod\limits_{i = 1}^{n}a_i\right) [x^{n - 2}] \prod \limits_{i = 1}^{n}F(a_ix) \end{aligned} ((n2)!(2n2)i=1nai)[xn2]i=1nF(aix)
  • 难点在于 ∏ i = 1 n F ( a i x ) ( m o d    x n − 1 ) \prod \limits_{i = 1}^{n}F(a_ix) (\mod x^{n - 1}) i=1nF(aix)(modxn1) 怎么计算。
  • 注意到求和比乘积容易很多,可以先对所有 F F F ln ⁡ \ln ln 求和然后再 exp ⁡ \exp exp 回来,这样就可以把 a i a_i ai 提取出来,答案可以转化为:
    ( ( n − 2 ) ! ( 2 n − 2 ) ∏ i = 1 n a i ) [ x n − 2 ] exp ⁡ ( ∑ j = 0 n − 2 [ x j ] ln ⁡ F ( x ) ∑ i = 1 n a i j ) \begin{aligned} \left((n - 2)!(2n - 2) \prod \limits_{i = 1}^{n}a_i\right)[x^{n - 2}] \exp\left( \sum \limits_{j = 0}^{n - 2}[x^{j}]\ln F(x) \sum\limits_{i = 1}^{n}a_i^j\right) \end{aligned} ((n2)!(2n2)i=1nai)[xn2]exp(j=0n2[xj]lnF(x)i=1naij)
  • 现在只需要对于每个 j ( 0 ≤ j ≤ n − 2 ) j(0 \le j \le n - 2) j(0jn2),求出 ∑ i = 1 n a i j \sum \limits_{i = 1}^{n}a_i^j i=1naij
  • 考虑生成函数
    H i ( x ) = ∑ j = 0 ∞ a i j x j = 1 1 − a i x \begin{aligned} H_i(x) = \sum \limits_{j = 0}^{\infty} {a_i^jx^j} = \frac{1}{1 - a_ix} \end{aligned} Hi(x)=j=0aijxj=1aix1
  • 我们所要求的就是
    H ( x ) = ∑ i = 1 n H i ( x ) = ∑ i = 1 n 1 1 − a i x ( m o d    x n − 1 ) \begin{aligned} H(x)=\sum \limits_{i = 1}^{n} H_i(x) = \sum \limits_{i=1}^{n}\frac{1}{1-a_ix} (\mod x^{n - 1})\\ \end{aligned} H(x)=i=1nHi(x)=i=1n1aix1(modxn1)
  • 可以用类似 分治FFT 的过程模拟分式的相加,求出分子和分母的多项式,对分母求逆后与分子相乘。
  • 时间复杂度 O ( n log ⁡ n ) \mathcal O(n \log n) O(nlogn),结合 Algorithm 2 \text{Algorithm 2} Algorithm 2 Algorithm 3 \text{Algorithm 3} Algorithm 3 期望得分 70pts \text{70pts} 70pts

Algorithm 5

  • 不难发现,从 Algorithm 4 \text{Algorithm 4} Algorithm 4 到正解已经没有什么瓶颈了。
  • 首先把不等于的限制去掉,答案可变为:
    ( ( n − 2 ) ! ∏ i = 1 n a i ) [ x n − 2 ] ( ( ∑ i = 1 n G F ( a i x ) ) ( ∏ i = 1 n F ( a i x ) ) ) \begin{aligned} \left((n - 2)! \prod \limits_{i = 1}^{n} a_i \right) [x^{n - 2}]\left(\left(\sum \limits_{i = 1}^{n}\frac{G}{F}(a_ix) \right) \left(\prod \limits_{i = 1}^{n} F(a_ix)\right)\right)\\ \end{aligned} ((n2)!i=1nai)[xn2]((i=1nFG(aix))(i=1nF(aix)))
  • 类似 Algorithm 4 \text{Algorithm 4} Algorithm 4 中处理自变量中含有 a a a 的情况:
    ( ( n − 2 ) ! ∏ i = 1 n a i ) [ x n − 2 ] ( ( ∑ i = 0 n − 2 ( [ x i ] G F ( x ) ) ( ∑ j = 1 n a j i ) ) exp ⁡ ( ∑ i = 0 n − 2 [ x i ] ln ⁡ F ( x ) ( ∑ j = 1 n a j i ) ) ) \begin{aligned} \left((n - 2)! \prod \limits_{i = 1}^{n} a_i \right) [x^{n - 2}] \left(\left(\sum \limits_{i = 0}^{n - 2} \left([x^i]\frac{G}{F}(x)\right) \left(\sum \limits_{j = 1}^{n}a_j^i \right)\right)\exp\left(\sum \limits_{i = 0}^{n-2} [x^i]\ln F(x) \left(\sum \limits_{j = 1}^{n}a_j^i \right)\right)\right) \\ \end{aligned} ((n2)!i=1nai)[xn2]((i=0n2([xi]FG(x))(j=1naji))exp(i=0n2[xi]lnF(x)(j=1naji)))
  • 简化一下得到:
    ( ( n − 2 ) ! ∏ i = 1 n a i ) [ x n − 2 ] ( ( ∑ i = 0 n − 2 [ x i ] G F ( x ) [ x i ] H ( x ) ) exp ⁡ ( ∑ i = 0 n − 2 [ x i ] ln ⁡ F ( x ) [ x i ] H ( x ) ) ) \begin{aligned} \left((n - 2)! \prod \limits_{i = 1}^{n} a_i \right) [x^{n - 2}] \left(\left(\sum \limits_{i = 0}^{n - 2} [x^i]\frac{G}{F}(x) [x^i]H(x)\right)\exp\left(\sum \limits_{i = 0}^{n - 2} [x^i]\ln F(x) [x^i]H(x)\right)\right) \\ \end{aligned} ((n2)!i=1nai)[xn2]((i=0n2[xi]FG(x)[xi]H(x))exp(i=0n2[xi]lnF(x)[xi]H(x)))
  • 直接计算即可,时间复杂度 O ( n log ⁡ 2 n + n ln ⁡ n log ⁡ m ) \mathcal O(n \log^2 n + \frac{n}{\ln n} \log m) O(nlog2n+lnnnlogm),期望得分 100pts \text{100pts} 100pts

Code 2

  • 100pts \text{100pts} 100pts
#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	char ch;
	while (ch = getchar(), !isdigit(ch));
	res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
		res = res * 10 + ch - 48;
}

using std::vector;
const int N = 3e4 + 5;
const int N4 = 12e4 + 5; 
const int N8 = 24e4 + 5;
const int mod = 998244353;
const int inv3 = (mod + 1) / 3;
bool vis[N];
int tw[N8], rev[N8], inv[N8];
int a[N], ex_m[N], ex_2m[N], pri[N], fra[N], inv_fra[N];
int pr, n, m;

template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}

inline int quick_pow(int x, int k)
{
	int res = 1;
	while (k)
	{
		if (k & 1)
			res = 1ll * res * x % mod;
		x = 1ll * x * x % mod;
		k >>= 1;
	}
	return res;
}

inline void add(int &x, int y)
{
	x += y;
	x >= mod ? x -= mod : 0;
}

inline void dec(int &x, int y)
{
	x -= y;
	x < 0 ? x += mod : 0;
}

struct poly
{
	vector<int> f;
	int fm;

	inline void NTT(int opt)
	{
		int g = opt == 1 ? 3 : inv3;
		for (int i = 0; i < fm; ++i)
			if (i < rev[i])
				std::swap(f[i], f[rev[i]]);
		for (int k = 1; k < fm; k <<= 1)
		{
			int w = quick_pow(g, (mod - 1) / (k << 1));
			tw[0] = 1;
			for (int i = 1; i < k; ++i)
				tw[i] = 1ll * tw[i - 1] * w % mod;
			for (int i = 0; i < fm; i += k << 1)
				for (int j = 0; j < k; ++j)
				{
					int &x = f[i + j],
						&y = f[i + j + k];
					int u = x, 
						v = 1ll * tw[j] * y % mod;
					x = y = u;
					add(x, v);
					dec(y, v);
				}
		}
		if (opt == -1)
		{
			for (int i = 0, inv = quick_pow(fm, mod - 2); i < fm; ++i)
				f[i] = 1ll * f[i] * inv % mod;
		}
	}
}tr1[N4], tr2[N4], sum;

poly inv_a, exp_a, ln_a, _a, b;
poly F, G, tmp;

inline poly operator * (poly a, poly b)
{
	int tot = a.fm + b.fm, fm, k = -1;
	for (fm = 1; fm <= tot; fm <<= 1, ++k);
	for (int i = 1; i < fm; ++i)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << k);
	
	a.fm = fm;
	a.f.resize(fm);
	b.fm = fm;
	b.f.resize(fm);

	a.NTT(1);
	b.NTT(1);
	for (int i = 0; i < fm; ++i)
		a.f[i] = 1ll * a.f[i] * b.f[i] % mod;
	a.NTT(-1);
	a.fm = tot;
	a.f.resize(tot + 1);
	return a;
}

inline poly operator * (poly a, const int &k)
{
	for (int i = 0; i <= a.fm; ++i)
		a.f[i] = 1ll * a.f[i] * k % mod;
	return a;
}

inline poly operator + (poly a, poly b)
{
	int fm = Max(a.fm, b.fm);
	a.f.resize(fm + 1);
	b.f.resize(fm + 1);
	for (int i = 0; i <= fm; ++i)
		add(a.f[i], b.f[i]);
	a.fm = fm;
	return a;
}

inline poly operator / (poly a, poly b)
{
	for (int i = 0; i <= a.fm; ++i)
		a.f[i] = 1ll * a.f[i] * b.f[i] % mod;
	return a;
}

inline poly poly_inv(poly a)
{
	inv_a.fm = 0;
	inv_a.f.resize(1);
	inv_a.f[0] = quick_pow(a.f[0], mod - 2);
	int k = 1, cnt = 0;
	while (k <= a.fm)
	{
		k <<= 1, ++cnt;
		int fm = k << 1;
		for (int i = 1; i < fm; ++i)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << cnt);

		inv_a.fm = fm;
		inv_a.f.resize(fm);
		inv_a.NTT(1);

		_a.fm = fm;
		_a.f.resize(fm);
		for (int i = 0; i < k; ++i)
			_a.f[i] = i <= a.fm ? a.f[i] : 0;
		_a.NTT(1);

		for (int i = 0; i < fm; ++i)
		{
			int tmp = inv_a.f[i];
			add(inv_a.f[i], tmp);
			dec(inv_a.f[i], 1ll * _a.f[i] * tmp % mod * tmp % mod);
		}
		
		inv_a.NTT(-1);
		inv_a.fm = k;
		inv_a.f.resize(k);
		_a.f.clear();
	}
	inv_a.fm = a.fm;
	inv_a.f.resize(a.fm + 1);
	return inv_a;
}

inline poly poly_der(poly a)
{
	for (int i = 0; i < a.fm; ++i)
		a.f[i] = 1ll * a.f[i + 1] * (i + 1) % mod;
	--a.fm;
	a.f.resize(a.fm + 1);
	return a;
}

inline poly poly_int(poly a)
{
	++a.fm;
	a.f.resize(a.fm + 1);	
	for (int i = a.fm; i >= 1; --i)
		a.f[i] = 1ll * a.f[i - 1] * inv[i] % mod;
	a.f[0] = 0;
	return a;
}

inline poly poly_ln(poly a)
{
	b = poly_der(a) * poly_inv(a);
	b.fm = a.fm - 1;
	b.f.resize(a.fm);
	return poly_int(b);
}

inline poly poly_exp(poly a)
{
	exp_a.fm = 0;
	exp_a.f.resize(1);
	exp_a.f[0] = 1;

	int k = 1, cnt = 0;
	while (k <= a.fm)
	{
		ln_a.f.clear();
		inv_a.f.clear();
		b.f.clear();

		k <<= 1, ++cnt;
		int fm = k << 1;
		
		exp_a.fm = k - 1;
		exp_a.f.resize(k);
		ln_a = poly_ln(exp_a);
		
		ln_a.fm = fm;
		ln_a.f.resize(fm);
		exp_a.fm = fm;
		exp_a.f.resize(fm);
		for (int i = 0; i < k; ++i)
		{
			ln_a.f[i] ? ln_a.f[i] = mod - ln_a.f[i] : 0;
			if (i <= a.fm)
				add(ln_a.f[i], a.f[i]);
		}
		add(ln_a.f[0], 1);
		
		for (int i = 1; i < fm; ++i)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << cnt);
		ln_a.NTT(1);
		exp_a.NTT(1);
		for (int i = 0; i < fm; ++i)
			exp_a.f[i] = 1ll * exp_a.f[i] * ln_a.f[i] % mod;
		exp_a.NTT(-1);

		exp_a.fm = k - 1;
		exp_a.f.resize(k);
		ln_a.f.clear();
	}
	exp_a.fm = a.fm;
	exp_a.f.resize(a.fm + 1);
	return exp_a;
}

#define sL s << 1
#define sR s << 1 | 1

inline void solve(int s, int l, int r)
{
	if (l == r)
	{
		tr1[s].fm = 0;
		tr1[s].f.resize(1);
		tr1[s].f[0] = 1;

		tr2[s].fm = 1;
		tr2[s].f.resize(2);
		tr2[s].f[0] = 1; 
		dec(tr2[s].f[1], a[l]);
		return ; 
	}

	int mid = l + r >> 1;
	solve(sL, l, mid);
	solve(sR, mid + 1, r);
	tr1[s] = tr1[sL] * tr2[sR] + tr2[sL] * tr1[sR];
	tr2[s] = tr2[sL] * tr2[sR];
}

int main()
{
	read(n); read(m);
	if (n == 1)
		return puts(m == 0 ? "1" : "0"), 0;
	
	fra[0] = 1;
	for (int i = 1; i < n; ++i)
		fra[i] = 1ll * fra[i - 1] * i % mod;
	inv_fra[n - 1] = quick_pow(fra[n - 1], mod - 2);
	for (int i = n - 1; i >= 1; --i)
		inv_fra[i - 1] = 1ll * inv_fra[i] * i % mod;
	inv[0] = inv[1] = 1;
	for (int i = 2, im = n << 2; i <= im; ++i)
		inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;

	int prod = fra[n - 2];
	for (int i = 1; i <= n; ++i)
	{
		read(a[i]);
		prod = 1ll * prod * a[i] % mod;
	}

	ex_m[1] = 1;
	for (int i = 1; i < n; ++i)
	{
		if (!vis[i])
		{
			pri[++pr] = i;
			ex_m[i] = quick_pow(i, m);
		}
		for (int j = 1; j <= pr && 1ll * pri[j] * i < n; ++j)
		{
			int tmp = pri[j] * i;
			vis[tmp] = true;
			ex_m[tmp] = 1ll * ex_m[i] * ex_m[pri[j]] % mod; 
			if (i % pri[j] == 0)
				break ;
		}
	}
	for (int i = 1; i < n; ++i)
		ex_2m[i] = 1ll * ex_m[i] * ex_m[i] % mod;

	solve(1, 1, n);
	sum = tr1[1] * poly_inv(tr2[1]);
	sum.fm = n - 2;
	sum.f.resize(n - 1);

	F.fm = n - 2;
	F.f.resize(n - 1);
	G.fm = n - 2;
	G.f.resize(n - 1);
	for (int i = 0; i <= n - 2; ++i)
		F.f[i] = 1ll * ex_m[i + 1] * inv_fra[i] % mod;
	for (int i = 0; i <= n - 2; ++i)
		G.f[i] = 1ll * ex_2m[i + 1] * inv_fra[i] % mod;

	tmp = G * poly_inv(F);
	tmp.fm = n - 2;
	tmp.f.resize(n - 1);
	printf("%d\n", 1ll * prod * ((tmp / sum) * poly_exp(poly_ln(F) / sum)).f[n - 2] % mod);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值