[LOJ#2983][WC2019]数树

Address

Solution

  • 这是一道计数 (shen) 好 (xian) 题,综合性很强 ,虽然是三合一
  • 鉴于此题用到的算法较多,我就不在标题里列出了,此题用到的算法及技巧如下
  • 容斥原理
  • prufer 序列计数
  • 组合数学
  • 树形 DP
  • 多项式 exp

Task 1 : o p = 0 op=0 op=0

  • 首先分析下问题,很容易得出,如果设 T 0 T_0 T0 表示一个 n n n 个点的森林,边集为两棵树 T 1 T_1 T1 T 2 T_2 T2 的边交集,且 T 0 T_0 T0 包含 c n t cnt cnt 个连通块,则答案为
  • y c n t y^{cnt} ycnt
  • 利用 map 等很多技巧都可以求两棵树的边交集
  • O ( n log ⁡ n ) O(n\log n) O(nlogn)

Task 2 : o p = 1 op=1 op=1

前置知识:已经确定一些边的生成树计数

  • 这个问题大致为:有 m m m 个连通块,第 i i i 个连通块包含 a i a_i ai 个点, n = ∑ i = 1 m a i n=\sum_{i=1}^ma_i n=i=1mai
  • 求用 m − 1 m-1 m1 条边连接 m m m 个连通块的方案数
  • 考虑一种对树进行计数的工具 prufer 序列
  • 先考虑,如果对于所有的 i i i 都有 a i = 1 a_i=1 ai=1 ,那么长度为 m − 2 m-2 m2 的 prufer 序列每个元素都可以取 m m m 个点中的任一个,方案数 m m − 2 m^{m-2} mm2
  • 此外, prufer 序列的另一个性质:度数为 d d d 的点在序列中出现 d − 1 d-1 d1
  • 回到 a i a_i ai 不一定等于 1 1 1 的情况,设第 i i i 的点的度数为 d i d_i di
  • 那么第 i i i 个连通块会让方案数额外乘上 a i d i a_i^{d_i} aidi
  • 我们可以把过程看成如下
  • 初始时设 r e s = ∏ i = 1 m a i res=\prod_{i=1}^ma_i res=i=1mai
  • 然后考虑 prufer 序列的第 i i i 个元素 p i p_i pi
  • 如果 p i p_i pi u u u ,那么会为方案数乘上 a u a_u au ,而 p i p_i pi 可以取 m m m 个连通块中的任一,故将 r e s res res 乘上 n n n
  • 注意到处理 prufer 的过程中,第 i i i 个连通块贡献了 d i − 1 d_i-1 di1 a i a_i ai,再乘上初始的 ∏ i = 1 m a i \prod_{i=1}^ma_i i=1mai 恰好是贡献 d i d_i di
  • 所以方案数为
  • r e s = n m − 2 ∏ i = 1 m a i res=n^{m-2}\prod_{i=1}^ma_i res=nm2i=1mai

回到问题

  • 我们知道,对于森林,有:边数 + + + 连通块个数 = = = 点数
  • C ( S ) C(S) C(S) 表示树 T 2 T_2 T2 有多少种取法使得 T 1 T_1 T1 T 2 T_2 T2 的边交集恰好为 S S S
  • 那么答案显然为
  • ∑ S C ( S ) × y n − ∣ S ∣ \sum_SC(S)\times y^{n-|S|} SC(S)×ynS
  • 考虑利用容斥化一下 C ( S ) C(S) C(S)
  • D ( S ) D(S) D(S) 表示树 T 2 T_2 T2 有多少种取法使得边集 S S S T 1 T_1 T1 T 2 T_2 T2 的边交集的子集
  • 那么 D ( S ) D(S) D(S) 显然比 C ( S ) C(S) C(S) 容易直接求。这就相当于 T 1 T_1 T1 T 2 T_2 T2 都必须包含边集 S S S
  • 于是 D ( S ) D(S) D(S) 就相当于有 ∣ S ∣ |S| S 条边已经确定的情况下,在剩下的边中选 n − 1 − ∣ S ∣ n-1-|S| n1S 条,连通所有点的方案数,可以用上面介绍的 prufer 序列计数求得
  • 于是求 C ( S ) C(S) C(S) 我们可以容斥
  • C ( S ) = ∑ S ⊂ T ( − 1 ) ∣ T ∣ − ∣ S ∣ D ( T ) C(S)=\sum_{S\subset T}(-1)^{|T|-|S|}D(T) C(S)=ST(1)TSD(T)
  • 代入答案的式子
  • a n s w e r = y n ∑ S ( y − 1 ) ∣ S ∣ C ( S ) answer=y^n\sum_S(y^{-1})^{|S|}C(S) answer=ynS(y1)SC(S)
  • = y n ∑ S ( y − 1 ) ∣ S ∣ ∑ S ⊂ T ( − 1 ) ∣ T ∣ − ∣ S ∣ D ( T ) =y^n\sum_S(y^{-1})^{|S|}\sum_{S\subset T}(-1)^{|T|-|S|}D(T) =ynS(y1)SST(1)TSD(T)
  • T T T 集合提到外层枚举
  • = y n ∑ T D ( T ) ∑ S ⊂ T ( y − 1 ) ∣ S ∣ ( − 1 ) ∣ T ∣ − ∣ S ∣ =y^n\sum_TD(T)\sum_{S\subset T}(y^{-1})^{|S|}(-1)^{|T|-|S|} =ynTD(T)ST(y1)S(1)TS
  • 发现在枚举 S ⊂ T S\subset T ST 时, ∣ S ∣ |S| S 相同的集合 S S S 本质上没有区别
  • = y n ∑ T D ( T ) ∑ i = 0 ∣ T ∣ ( ∣ T ∣ i ) ( y − 1 ) i ( − 1 ) ∣ T ∣ − i =y^n\sum_TD(T)\sum_{i=0}^{|T|}\binom{|T|}i(y^{-1})^i(-1)^{|T|-i} =ynTD(T)i=0T(iT)(y1)i(1)Ti
  • 由二项式定理得
  • = y n ∑ T D ( T ) ( y − 1 − 1 ) ∣ T ∣ =y^n\sum_TD(T)(y^{-1}-1)^{|T|} =ynTD(T)(y11)T
  • 注意这里的 T T T 只能取树 T 1 T_1 T1 的边子集
  • 发现当 y = 1 y=1 y=1 时会对我们的处理造成不便,直接特判掉: y = 1 y=1 y=1 时答案为 n n − 2 n^{n-2} nn2
  • 然后继续转化上式,设 R ( S ) R(S) R(S) 表示 n n n 个点,加入树 T 1 T_1 T1 中除边集 S S S 之外的所有边之后,每个连通块大小之积,根据上面介绍的 prufer 序列计数,可以得到
  • = y n ∑ S n ∣ S ∣ − 1 R ( S ) ( y − 1 − 1 ) n − ( ∣ S ∣ + 1 ) =y^n\sum_Sn^{|S|-1}R(S)(y^{-1}-1)^{n-(|S|+1)} =ynSnS1R(S)(y11)n(S+1)
  • = ( y ( y − 1 − 1 ) ) n n 2 ∑ S R ( S ) ( n ( y − 1 − 1 ) − 1 ) ∣ S ∣ + 1 =\frac{(y(y^{-1}-1))^n}{n^2}\sum_SR(S)(n(y^{-1}-1)^{-1})^{|S|+1} =n2(y(y11))nSR(S)(n(y11)1)S+1
  • ( y ( y − 1 − 1 ) ) n n 2 \frac{(y(y^{-1}-1))^n}{n^2} n2(y(y11))n 这一部分可以在算好后面的部分之后再去乘上。我们设 Y = n ( y − 1 − 1 ) − 1 Y=n(y^{-1}-1)^{-1} Y=n(y11)1 ,那么 ∑ S R ( S ) Y ∣ S ∣ + 1 \sum_SR(S)Y^{|S|+1} SR(S)YS+1 就相当于把树 T 1 T_1 T1 分成几个连通块,分成 i i i 个连通块的贡献为 Y i Y^i Yi 乘上所有连通块的大小之积
  • 这显然可以用树形 DP 解决
  • 发现由于需要考虑所有连通块的大小之积,所以这个 DP 是 O ( n 2 ) O(n^2) O(n2)
  • 我们不妨寻找一个 R ( S ) Y ∣ S ∣ + 1 R(S)Y^{|S|+1} R(S)YS+1 的组合意义
  • 这相当于在 ∣ S ∣ + 1 |S|+1 S+1 个连通块中,每个连通块内各选一点,选一个点会乘上 Y Y Y 的贡献
  • 于是 ∑ S R ( S ) Y ∣ S ∣ + 1 \sum_SR(S)Y^{|S|+1} SR(S)YS+1 就相当于选出一些点,若选出了 i i i 个点,那么贡献为 Y i Y^i Yi 乘上选出 i − 1 i-1 i1 条边切掉使得这 i i i 个点两两不连通的方案数
  • 于是我们可以开始 DP 了
  • f [ u ] f[u] f[u] 表示 u u u 的子树内选出一些点,使得这些点归属不同的连通块
  • g [ u ] g[u] g[u] 表示 u u u 的子树内选出一些点(不能选 u u u ),使得 u u u 归属一个连通块,选出的点各自位于一个连通块
  • 如果 u u u 的子树只有一个点则 f [ u ] = Y f[u]=Y f[u]=Y g [ u ] = 1 g[u]=1 g[u]=1
  • 使用背包合并的方式转移,如果 u u u 处理到子树 v v v 之前的 DP 数组为 f ′ f' f g ′ g' g ,则
  • f [ u ] = f ′ [ u ] × f [ v ] + g ′ [ u ] × f [ v ] + f ′ [ u ] × g [ v ] f[u]=f'[u]\times f[v]+g'[u]\times f[v]+f'[u]\times g[v] f[u]=f[u]×f[v]+g[u]×f[v]+f[u]×g[v]
  • g [ u ] = g ′ [ u ] × f [ v ] + g ′ [ u ] × g [ v ] g[u]=g'[u]\times f[v]+g'[u]\times g[v] g[u]=g[u]×f[v]+g[u]×g[v]
  • 即转移时分 ( u , v ) (u,v) (u,v) 切掉与不切掉进行讨论
  • 于是答案为
  • ( y ( y − 1 − 1 ) ) n n 2 f [ R o o t ] \frac{(y(y^{-1}-1))^n}{n^2}f[Root] n2(y(y11))nf[Root]
  • O ( n ) O(n) O(n)

Task 3 : o p = 2 op=2 op=2

前置知识:指数生成函数与多项式 EXP

  • 定义组合对象 A A A 的指数生成函数 F ( x ) F(x) F(x)
  • F ( x ) = ∑ i ≥ 0 A i i ! F(x)=\sum_{i\ge 0}\frac{A_i}{i!} F(x)=i0i!Ai
  • 可以得出,如果组合对象 A A A B B B 的指数生成函数分别为 F ( x ) F(x) F(x) G ( x ) G(x) G(x) ,那么将这两个组合对象进行带标号拼接得到 C C C ,则 C C C 的指数生成函数 H ( x ) H(x) H(x)
  • [ x n ] H ( x ) = C n n ! = ∑ i + j = n ( n i ) A i B j ( n ! ) − 1 [x^n]H(x)=\frac{C_n}{n!}=\sum_{i+j=n}\binom niA_iB_j(n!)^{-1} [xn]H(x)=n!Cn=i+j=n(in)AiBj(n!)1
  • = ∑ i + j = n [ x i ] F ( x ) [ x j ] G ( x ) =\sum_{i+j=n}[x^i]F(x)[x^j]G(x) =i+j=n[xi]F(x)[xj]G(x)
  • H ( x ) = F ( x ) G ( x ) H(x)=F(x)G(x) H(x)=F(x)G(x)
  • 然后引入多项式 EXP
  • 设组合对象 A A A 的指数生成函数 F ( x ) F(x) F(x) A 0 = 0 A_0=0 A0=0 ),有
  • e F ( x ) = ∑ i ≥ 0 F i ( x ) i ! e^{F(x)}=\sum_{i\ge 0}\frac{F^i(x)}{i!} eF(x)=i0i!Fi(x)
  • 显然 F i ( x ) F^i(x) Fi(x) 表示 i i i 个组合对象 A A A 带标号拼接
  • F i ( x ) i ! \frac{F^i(x)}{i!} i!Fi(x) 表示 i i i 个组合对象 A A A 不考虑彼此顺序的拼接
  • 于是 e F ( x ) e^{F(x)} eF(x) 表示一些组合对象 A A A 进行不考虑彼此顺序的拼接
  • 以上概念可能有些含糊,所以我们引入一个例子
  • 如,无向简单图(不含重边和自环)是由若干无向简单连通图构成的
  • 连通块之间没有顺序
  • 所以若无向简单图个数的指数生成函数为 G ( x ) G(x) G(x) ,无向简单连通图个数的指数生成函数为 F ( x ) F(x) F(x) ,则
  • G ( x ) = e F ( x ) G(x)=e^{F(x)} G(x)=eF(x)
  • 显然 G ( x ) G(x) G(x) 是个形式幂级数,我们如何求得 G ( x )   m o d   x n G(x)\bmod x^n G(x)modxn 呢?
  • 由牛顿迭代得
  • G k + 1 ( x ) = G k ( x ) ( 1 − ln ⁡ G k ( x ) + F ( x ) ) G_{k+1}(x)=G_k(x)(1-\ln G_k(x)+F(x)) Gk+1(x)=Gk(x)(1lnGk(x)+F(x))
  • 其中 G k ( x ) = G ( x )   m o d   x 2 k , G 0 ( x ) = 1 G_k(x)=G(x)\bmod x^{2^k},G_0(x)=1 Gk(x)=G(x)modx2k,G0(x)=1
  • k k k 1 1 1 迭代到 ≥ n \ge n n 为止即可。复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

回到问题

  • 特判掉 y = 1 y=1 y=1 时答案为 n 2 ( n − 2 ) n^{2(n-2)} n2(n2)
  • 前面的容斥推导基本一样,同样地
  • a n s w e r = y n ∑ T D ( T ) ( y − 1 − 1 ) ∣ T ∣ answer=y^n\sum_TD(T)(y^{-1}-1)^{|T|} answer=ynTD(T)(y11)T
  • 注意,这时候 D ( T ) D(T) D(T) 表示钦定边集 T T T n n n 个点生成树个数的平方(因为要确定两棵树),这里的 T T T 可以取任意不成环的边集
  • 易得 D ( T ) D(T) D(T) 等于 n n n 2 ( n − ∣ T ∣ − 2 ) 2(n-|T|-2) 2(nT2) 次幂,乘上所有连通块大小之积的平方,但如果我们枚举的是连通块分属情况而不是边集 T T T ,我们还需要考虑每个连通块长什么样
  • 我们尝试把枚举 T T T 改成枚举边集 T T T 的连通块分属情况,则
  • a n s w e r = ( y ( y − 1 − 1 ) ) n n 4 ∑ T s ( n 2 ( y − 1 − 1 ) − 1 ) ∣ T s ∣ ∏ i = 1 ∣ T s ∣ T s i T s i answer=\frac{(y(y^{-1}-1))^n}{n^4}\sum_{Ts}(n^2(y^{-1}-1)^{-1})^{|Ts|}\prod_{i=1}^{|Ts|}Ts_i^{Ts_i} answer=n4(y(y11))nTs(n2(y11)1)Tsi=1TsTsiTsi
  • 其中 T s Ts Ts 为一种连通块划分, ∣ T s ∣ |Ts| Ts 为连通块个数, T s i Ts_i Tsi 为第 i i i 个连通块大小, ∏ i = 1 ∣ T s ∣ T s i T s i \prod_{i=1}^{|Ts|}Ts_i^{Ts_i} i=1TsTsiTsi 即为对划分方案 T s Ts Ts 来说,每个连通块的生成树个数之积,再乘上每个连通块大小之积的平方
  • ∑ \sum 后面的东西进行考虑,如果只有一个大小为 i i i 的连通块,设
  • Y = n 2 ( y − 1 − 1 ) − 1 Y=n^2(y^{-1}-1)^{-1} Y=n2(y11)1
  • f [ i ] = i i × Y f[i]=i^i\times Y f[i]=ii×Y
  • F ( x ) F(x) F(x) f [ i ] f[i] f[i] 的指数生成函数,那么发现 ∑ T s ( n 2 ( y − 1 − 1 ) − 1 ) ∣ T s ∣ ∏ i = 1 ∣ T s ∣ T s i T s i \sum_{Ts}(n^2(y^{-1}-1)^{-1})^{|Ts|}\prod_{i=1}^{|Ts|}Ts_i^{Ts_i} Ts(n2(y11)1)Tsi=1TsTsiTsi 其实就等于 n ! [ x n ] e F ( x ) n![x^n]e^{F(x)} n![xn]eF(x) ,直接套用多项式 exp 的板子即可
  • O ( n log ⁡ n ) O(n\log n) O(nlogn)

Code

#include <bits/stdc++.h>

// 20030830

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

template <class T>
inline void Swap(T &a, T &b) {T t = a; a = b; b = t;}

const int N = 1e5 + 5, M = N << 1, L = N * 7, ZZQ = 998244353,
INV2 = 499122177, INV4 = 748683265;

int n, y, op;

int qpow(int a, int b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = 1ll * res * a % ZZQ;
		a = 1ll * a * a % ZZQ;
		b >>= 1;
	}
	return res;
}

namespace solve0
{
	int fa[N], ans;
	std::map<int, int> app[N];
	
	int cx(int x)
	{
		if (fa[x] != x) fa[x] = cx(fa[x]);
		return fa[x];
	}
	
	void zm(int x, int y)
	{
		int ix = cx(x), iy = cx(y);
		if (ix != iy) fa[iy] = ix;
	}
	
	void work()
	{
		int u, v;
		for (int i = 1; i <= n; i++) fa[i] = i;
		for (int i = 1; i < n; i++)
			u = read(), v = read(), app[u][v] = app[v][u] = 1;
		for (int i = 1; i < n; i++)
		{
			u = read(); v = read();
			if (app[u][v]) zm(u, v);
		}
		for (int i = 1; i <= n; i++) if (cx(i) == i) ans++;
		printf("%d\n", qpow(y, ans));
	}
};

namespace solve1
{
	int ecnt, nxt[M], adj[N], go[M], f[N], g[N];

	void add_edge(int u, int v)
	{
		nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
		nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
	}
	
	void dfs(int u, int fu)
	{
		f[u] = y; g[u] = 1;
		for (int e = adj[u], v; e; e = nxt[e])
		{
			if ((v = go[e]) == fu) continue;
			dfs(v, u);
			int tf = (1ll * f[u] * f[v] + 1ll * g[u] * f[v]
				+ 1ll * f[u] * g[v]) % ZZQ,
				tg = (1ll * g[u] * f[v] + 1ll * g[u] * g[v]) % ZZQ;
			f[u] = tf; g[u] = tg;
		}
	}
	
	void work()
	{
		int u, v;
		for (int i = 1; i < n; i++)
			u = read(), v = read(), add_edge(u, v);
		if (y == 1) return (void) printf("%d\n", qpow(n, n - 2));
		int st = 1ll * y * (qpow(y, ZZQ - 2) - 1) % ZZQ;
		y = 1ll * qpow(qpow(y, ZZQ - 2) - 1, ZZQ - 2) * n % ZZQ;
		dfs(1, 0);
		printf("%d\n", 1ll * qpow(st, n) * f[1] % ZZQ
			* qpow(1ll * n * n % ZZQ, ZZQ - 2) % ZZQ);
	}
};

namespace solve2
{
	int fac[L], inv[L], invf[L], f[L], yp[L], rev[L], ta[L], tb[L],
	tmpf[L], tmpg[L], da[L], inva[L], tf[L], tg[L], tln[L], ans[L];
	
	void FFT(int n, int *a, int op)
	{
		for (int i = 0; i < n; i++) if (i < rev[i]) Swap(a[i], a[rev[i]]);
		yp[n] = qpow(3, (ZZQ - 1) / n * ((n + op) % n));
		for (int i = n >> 1; i; i >>= 1)
			yp[i] = 1ll * yp[i << 1] * yp[i << 1] % ZZQ;
		for (int k = 1; k < n; k <<= 1)
		{
			int x = yp[k << 1];
			for (int i = 0; i < n; i += k << 1)
			{
				int w = 1;
				for (int j = 0; j < k; j++)
				{
					int u = a[i + j], v = 1ll * w * a[i + j + k] % ZZQ;
					a[i + j] = (u + v) % ZZQ;
					a[i + j + k] = (u - v + ZZQ) % ZZQ;
					w = 1ll * w * x % ZZQ;
				}
			}
		}
	}
	
	void polymul(int n, int *a, int *b, int *res)
	{
		int ff = 1, tot = 0;
		while (ff < (n << 1) - 1) ff <<= 1, tot++;
		for (int i = 0; i < ff; i++)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
		for (int i = 0; i < ff; i++)
		{
			ta[i] = i < n ? a[i] : 0;
			tb[i] = i < n ? b[i] : 0;
		}
		FFT(ff, ta, 1); FFT(ff, tb, 1);
		for (int i = 0; i < ff; i++) ta[i] = 1ll * ta[i] * tb[i] % ZZQ;
		FFT(ff, ta, -1);
		int gg = qpow(ff, ZZQ - 2);
		for (int i = 0; i < ff; i++) res[i] = 1ll * ta[i] * gg % ZZQ;
	}
	
	void polyinv(int n, int *a, int *res)
	{
		res[0] = qpow(a[0], ZZQ - 2);
		int ff = 4, tot = 2, gg = INV4;
		for (int k = 1; k < n; k <<= 1)
		{
			for (int i = 0; i < ff; i++)
				rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
			for (int i = 0; i < ff; i++)
			{
				tmpf[i] = i < n && i < (k << 1) ? a[i] : 0;
				tmpg[i] = i < k ? res[i] : 0;
			}
			FFT(ff, tmpf, 1); FFT(ff, tmpg, 1);
			for (int i = 0; i < ff; i++)
				tmpf[i] = 1ll * tmpg[i] * (ZZQ + 2 -
				1ll * tmpf[i] * tmpg[i] % ZZQ) % ZZQ;
			FFT(ff, tmpf, -1);
			for (int i = 0; i < (k << 1); i++)
				res[i] = 1ll * tmpf[i] * gg % ZZQ;
			ff <<= 1; tot++;
			gg = 1ll * gg * INV2 % ZZQ;
		}
	}
	
	void polyln(int n, int *a, int *res)
	{
		for (int i = 0; i < n; i++)
			da[(i + n - 1) % n] = 1ll * i * a[i] % ZZQ;
		polyinv(n, a, inva);
		polymul(n, da, inva, res);
		for (int i = n - 2; i >= 0; i--)
			res[i + 1] = 1ll * inv[i + 1] * res[i] % ZZQ;
		res[0] = 0;
	}
	
	void polyexp(int n, int *a, int *res)
	{
		res[0] = 1;
		int ff = 4, tot = 2, gg = INV4;
		for (int k = 1; k < n; k <<= 1)
		{
			for (int i = 0; i < ff; i++)
				tg[i] = i < k ? res[i] : 0;
			polyln(ff, tg, tln);
			for (int i = 0; i < ff; i++)
				tf[i] = i < n && i < (k << 1) ? a[i] : 0;
			for (int i = 0; i < ff; i++)
				rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
			for (int i = (k << 1); i < ff; i++) tln[i] = 0;
			FFT(ff, tg, 1); FFT(ff, tf, 1); FFT(ff, tln, 1);
			for (int i = 0; i < ff; i++)
				tf[i] = (1ll - tln[i] + tf[i] + ZZQ) * tg[i] % ZZQ;
			FFT(ff, tf, -1);
			for (int i = 0; i < (k << 1); i++)
				res[i] = 1ll * tf[i] * gg % ZZQ;
			ff <<= 1; tot++;
			gg = 1ll * gg * INV2 % ZZQ;
		}
	}
	
	void work()
	{
		if (y == 1) return (void) printf("%d\n", qpow(n, n - 2 << 1));
		int st = 1ll * y * (qpow(y, ZZQ - 2) - 1) % ZZQ;
		y = 1ll * qpow(qpow(y, ZZQ - 2) - 1, ZZQ - 2) * n % ZZQ * n % ZZQ;
		fac[0] = inv[1] = invf[0] = 1;
		for (int i = 1; i < L; i++)
			fac[i] = 1ll * fac[i - 1] * i % ZZQ;
		for (int i = 2; i < L; i++)
			inv[i] = 1ll * (ZZQ - ZZQ / i) * inv[ZZQ % i] % ZZQ;
		for (int i = 1; i < L; i++)
			invf[i] = 1ll * invf[i - 1] * inv[i] % ZZQ;
		for (int i = 1; i <= n; i++)
			f[i] = 1ll * qpow(i, i) * y % ZZQ * invf[i] % ZZQ;
		polyexp(n + 1, f, ans);
		printf("%d\n", 1ll * qpow(st, n) * ans[n] % ZZQ * fac[n] % ZZQ
			* qpow(n, ZZQ - 5) % ZZQ);
	}
};

int main()
{
	n = read(); y = read(); op = read();
	if (op == 0) solve0::work();
	else if (op == 1) solve1::work();
	else solve2::work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值