LOJ#2983. 「WC2019」数树

传送门

抄题解
T a s k 0 Task0 Task0,随便做一下,设 c n t cnt cnt 为相同的边的个数,输出 y n − c n t y^{n-cnt} yncnt
T a s k 1 Task1 Task1,给定其中一棵树
设初始答案为 y n y^n yn,首先可以发现,每有一条边和给定的树相同就会使得答案除去 y y y
那么可以利用矩阵树定理,已经有的边权值为 y − 1 y^{-1} y1,其它的连成完全图,权值为 1 1 1
求解行列式之后乘上 y n y^n yn 即可, O ( n 3 ) O(n^3) O(n3)
第一种正解 o r z   l a o f u orz~laofu orz laofu 即可 不会
第二种正解

一个小trick
对于求恰好 k k k 个的方案数,并且贡献为 x k x^k xk 的形式
可以利用二项式定理展开
x k = ∑ i = 0 k ( k i ) ( x − 1 ) k x^k=\sum_{i=0}^{k}\binom{k}{i}(x-1)^k xk=i=0k(ik)(x1)k
含义为枚举所有的集合 E E E,如果 E E E 是答案的子集,那么贡献为 ( x − 1 ) ∣ E ∣ (x-1)^{|E|} (x1)E,否则为 0 0 0
所有子集 E E E 的贡献之和恰好是 x k x^k xk,这样可以省去容斥

那么可以直接算 y − 1 − 1 y^{-1}-1 y11 在每一种两棵树都包含的边集 ∣ E ∣ |E| E 的贡献
下面的 y y y 默认为 y − 1 − 1 y^{-1}-1 y11
对于一种边集 ∣ E ∣ |E| E,假设它把图分成了 m m m 个连通块,每个连通块大小为 a i a_i ai
运用 p r u f e r prufer prufer 的知识,不难得到其生成树的方案数为 n m − 2 ∏ i = 1 m a i n^{m-2}\prod_{i=1}^{m}a_i nm2i=1mai
直观上来看这个东西,只能得到一个 O ( n 2 ) O(n^2) O(n2) d p dp dp f i , j f_{i,j} fi,j 表示 i i i 所在连通块,大小为 j j j
然后考虑 ∏ i = 1 m a i \prod_{i=1}^{m}a_i i=1mai 的组合意义,即从每一个连通块选出一个点的方案数
那么就可以把上面的 d p dp dp 改成 f i , 0 / 1 f_{i,0/1} fi,0/1 表示 i i i 所在连通块是否选择了一个点
O ( n ) O(n) O(n) 树形 d p dp dp 即可
T a s k 2 Task2 Task2,同样的利用那一个trick
f i f_i fi 表示钦定 i i i 条边相同的方案数
那么 a n s = ∑ i = 0 n − 1 f i y i ans=\sum_{i=0}^{n-1}f_iy^i ans=i=0n1fiyi
考虑计算 f i f_i fi,还是直接利用 p r u f e r prufer prufer,容易得到
f x = ∑ ∑ i = 1 n − x a i = n n ! ∏ i = 1 n − x a i a i − 2 a i ! ( n − x ) ! × ( n n − x − 2 ∏ i = 1 n − x a i ) 2 f_x=\sum_{\sum_{i=1}^{n-x}a_i=n}\frac{n!\prod_{i=1}^{n-x}\frac{a_i^{a_i-2}}{a_i!}}{(n-x)!}\times (n^{n-x-2}\prod_{i=1}^{n-x}a_i)^2 fx=i=1nxai=n(nx)!n!i=1nxai!aiai2×(nnx2i=1nxai)2
(含义参考:分配标号,连通块之间无序)

f x = ∑ ∑ i = 1 n − x a i = n n ! n 2 ( n − x − 2 ) ∏ i = 1 n − x a i a i a i ! m ! f_x=\sum_{\sum_{i=1}^{n-x}a_i=n}\frac{n!n^{2(n-x-2)}\prod_{i=1}^{n-x}\frac{a_i^{a_i}}{a_i!}}{m!} fx=i=1nxai=nm!n!n2(nx2)i=1nxai!aiai
根据这个式子不难得到一个 O ( n 3 ) O(n^3) O(n3) d p dp dp,枚举划分
继续推导,直接代到答案里面,可以得到
a n s = ∑ i = 1 n y n − i n 2 ( i − 2 ) i ! ∑ ∑ j = 1 i a j = n ∏ j = 1 i a j a j a j ! ans=\sum_{i=1}^{n}\frac{y^{n-i}n^{2(i-2)}}{i!}\sum_{\sum_{j=1}^{i}a_j=n}\prod_{j=1}^{i}\frac{a_j^{a_j}}{a_j!} ans=i=1ni!ynin2(i2)j=1iaj=nj=1iaj!ajaj
= y n n 4 ∑ i = 1 n n 2 i y i i ! ∑ ∑ j = 1 i a j = n ∏ j = 1 i a j a j a j ! =\frac{y^n}{n^4}\sum_{i=1}^{n}\frac{n^{2i}}{y^ii!}\sum_{\sum_{j=1}^{i}a_j=n}\prod_{j=1}^{i}\frac{a_j^{a_j}}{a_j!} =n4yni=1nyii!n2ij=1iaj=nj=1iaj!ajaj
∑ ∑ j = 1 i a j = n ∏ j = 1 i a j a j a j ! \sum_{\sum_{j=1}^{i}a_j=n}\prod_{j=1}^{i}\frac{a_j^{a_j}}{a_j!} j=1iaj=nj=1iaj!ajaj 可以写成生成函数的形式
[ x n ] ( ∑ j = 1 n j j j ! x j ) i [x^n](\sum_{j=1}^{n}\frac{j^j}{j!}x^j)^i [xn](j=1nj!jjxj)i
代入
a n s = y n n 4 ∑ i = 1 n n 2 i y i i ! [ x n ] ( ∑ j = 1 n j j j ! x j ) i ans=\frac{y^n}{n^4}\sum_{i=1}^{n}\frac{n^{2i}}{y^ii!}[x^n](\sum_{j=1}^{n}\frac{j^j}{j!}x^j)^i ans=n4yni=1nyii!n2i[xn](j=1nj!jjxj)i
= y n n 4 [ x n ] ∑ i = 1 n ( n 2 y ∑ j = 1 n j j j ! x j ) i i ! =\frac{y^n}{n^4}[x^n]\sum_{i=1}^{n}\frac{(\frac{n^2}{y}\sum_{j=1}^{n}\frac{j^j}{j!}x^j)^i}{i!} =n4yn[xn]i=1ni!(yn2j=1nj!jjxj)i
F = n 2 y ∑ j = 1 n j j j ! x j F=\frac{n^2}{y}\sum_{j=1}^{n}\frac{j^j}{j!}x^j F=yn2j=1nj!jjxj
忽略与 i i i 无关项,就是
[ x n ] ∑ i = 1 n F i i ! [x^n]\sum_{i=1}^{n}\frac{F^i}{i!} [xn]i=1ni!Fi
这个东西直接写成无穷项的形式不会影响答案,不难发现就是
[ x n ] e F [x^n]e^{F} [xn]eF
多项式 e x p exp exp 即可
O ( n l o g n ) O(nlogn) O(nlogn)

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

const int maxn(1e5 + 5);
const int mod(998244353);

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

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

inline int Add(int x, int y) {
    return x + y >= mod ? x + y - mod : x + y;
}

inline int Sub(int x, int y) {
    return x - y < 0 ? x - y + mod : x - y;
}

inline int Pow(ll x, int y) {
	ll ret = 1;
	for (; y; y >>= 1, x = x * x % mod)
		if (y & 1) ret = ret * x % mod;
	return ret;
}

int n, y, op;

namespace Task0 {
	map < pair <int, int> , int > vis;

	inline void Solve() {
		int i, u, v, cnt = 0;
		for (i = 1; i < n; ++i) {
			scanf("%d%d", &u, &v);
			if (u > v) swap(u, v);
			vis[make_pair(u, v)] = 1;
		}
		for (i = 1; i < n; ++i) {
			scanf("%d%d", &u, &v);
			if (u > v) swap(u, v);
			if (vis.count(make_pair(u, v))) ++cnt;
		}
		printf("%d\n", Pow(y, n - cnt));
	}
}

namespace Task1 {
	int first[maxn], cnt, f0[maxn], f1[maxn];

	struct Edge {
		int to, next;
	} edge[maxn << 1];

	inline void AddEdge(int u, int v) {
		edge[cnt] = (Edge){v, first[u]}, first[u] = cnt++;
		edge[cnt] = (Edge){u, first[v]}, first[v] = cnt++;
	}

	void Dfs(int u, int ff) {
		int e, v, tmp;
		f1[u] = f0[u] = 1;
		for (e = first[u]; ~e; e = edge[e].next)
			if ((v = edge[e].to) ^ ff) {
				Dfs(v, u), tmp = (ll)f1[u] * f1[v] % mod * n % mod;
				Inc(tmp, ((ll)f1[u] * f0[v] % mod + (ll)f0[u] * f1[v] % mod) * y % mod);
				f1[u] = tmp, tmp = (ll)f0[u] * f0[v] % mod * y % mod;
				Inc(tmp, (ll)f1[v] * f0[u] % mod * n % mod);
				f0[u] = tmp;
			}
	}

	inline void Solve() {
		int i, u, v, w = Pow(y, n), ans;
		y = Pow(y, mod - 2), Dec(y, 1);
		memset(first, -1, sizeof(first));
		for (i = 1; i < n; ++i) scanf("%d%d", &u, &v), AddEdge(u, v);
		Dfs(1, 0);
		ans = (ll)f1[1] * w % mod * Pow(n, mod - 2) % mod;
		printf("%d\n", ans);
	}
}

namespace Task2 {
	int f[maxn << 2], g[maxn << 2], ans, fac[maxn], inv[maxn];
	int w[2][maxn << 2], r[maxn << 2], l, deg;

	inline void Init(int len) {
		int i, x, y;
		for (l = 0, deg = 1; deg < len; deg <<= 1) ++l;
		for (i = 0; i < deg; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
		x = Pow(3, (mod - 1) / deg), y = Pow(x, mod - 2), w[0][0] = w[1][0] = 1;
		for (i = 1; i < deg; ++i) w[0][i] = (ll)w[0][i - 1] * x % mod, w[1][i] = (ll)w[1][i - 1] * y % mod;
	}

	inline void DFT(int *p, int opt) {
		int i, j, k, t, wn, x, y;
		for (i = 0; i < deg; ++i) if (r[i] < i) swap(p[r[i]], p[i]);
		for (i = 1; i < deg; i <<= 1)
			for (t = i << 1, j = 0; j < deg; j += t)
				for (k = 0; k < i; ++k) {
					wn = w[opt == -1][deg / t * k];
					x = p[j + k], y = (ll)p[j + k + i] * wn % mod;
					p[j + k] = Add(x, y), p[j + k + i] = Sub(x, y);
				}
		if (opt == -1) for (i = 0, wn = Pow(deg, mod - 2); i < deg; ++i) p[i] = (ll)p[i] * wn % mod;
	}

	void Inv(int *p, int *q, int len) {
		if (len == 1) {
			q[0] = Pow(p[0], mod - 2);
			return;
		}
		Inv(p, q, len >> 1);
		static int a[maxn << 2], b[maxn << 2];
		int tmp = len << 1, i;
		Init(tmp);
		for (i = 0; i < tmp; ++i) a[i] = b[i] = 0;
		for (i = 0; i < len; ++i) a[i] = p[i], b[i] = q[i];
		DFT(a, 1), DFT(b, 1);
		for (i = 0; i < tmp; ++i) a[i] = (ll)a[i] * b[i] % mod * b[i] % mod;
		DFT(a, -1);
		for (i = 0; i < len; ++i) q[i] = Sub(Add(q[i], q[i]), a[i]);
	}

	inline void Calc(int *p, int *q, int len) {
		int i;
		for (i = len - 2; ~i; --i) q[i + 1] = (ll)p[i] * Pow(i + 1, mod - 2) % mod;
		q[0] = 0;
	}

	inline void ICalc(int *p, int *q, int len) {
		int i;
		for (i = len - 2; ~i; --i) q[i] = (ll)p[i + 1] * (i + 1) % mod;
		q[len - 1] = 0;
	}

	inline void Ln(int *p, int *q, int len) {
		static int a[maxn << 2], b[maxn << 2];
		int tmp = len << 1, i;
		for (i = 0; i < tmp; ++i) a[i] = b[i] = 0;
		ICalc(p, a, len), Inv(p, b, len);
		DFT(a, 1), DFT(b, 1);
		for (i = 0; i < tmp; ++i) a[i] = (ll)a[i] * b[i] % mod;
		DFT(a, -1), Calc(a, q, len);
	}

	void Exp(int *p, int *q, int len) {
		if (len == 1) {
			q[0] = 1;
			return;
		}
		Exp(p, q, len >> 1);
		static int a[maxn << 2], b[maxn << 2];
		int tmp = len << 1, i;
		Init(tmp);
		for (i = 0; i < tmp; ++i) a[i] = b[i] = 0;
		Ln(q, a, len);
		for (i = 0; i < len; ++i) a[i] = Sub(p[i], a[i]), b[i] = q[i];
		Inc(a[0], 1), DFT(a, 1), DFT(b, 1);
		for (i = 0; i < tmp; ++i) a[i] = (ll)a[i] * b[i] % mod;
		DFT(a, -1);
		for (i = 0; i < len; ++i) q[i] = a[i];
	}

	inline void Solve() {
		int i, j, k, w = Pow(y, n), len;
		if (n == 1) {
			printf("%d\n", y);
			return;
		}
		if (y == 1) {
			printf("%d\n", Pow(n, (n - 2) << 1));
			return;
		}
		y = Pow(y, mod - 2), Dec(y, 1), fac[0] = inv[0] = inv[1] = 1;
		for (i = 1; i <= n; ++i) fac[i] = (ll)fac[i - 1] * i % mod;
		for (i = 2; i <= n; ++i) inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod;
		for (i = 2; i <= n; ++i) inv[i] = (ll)inv[i] * inv[i - 1] % mod;
		for (i = 1; i <= n; ++i) f[i] = (ll)Pow(i, i) * inv[i] % mod * Pow(y, mod - 2) % mod * n % mod * n % mod;
		for (len = 1; len <= n; len <<= 1);
		Exp(f, g, len);
		ans = (ll)g[n] * w % mod * fac[n] % mod * Pow(y, n) % mod * Pow(Pow(n, mod - 2), 4) % mod;
		printf("%d\n", ans);
	}
}

int main() {
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);
	scanf("%d%d%d", &n, &y, &op);
	if (!op) Task0 :: Solve();
	else if (op == 1) Task1 :: Solve();
	else Task2 :: Solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值