「JSOI2019」神经网络 (容斥)(组合数学)(生成函数)(树形DP)

传送门

题意:给 m m m 棵树,树之间是完全图,问哈密顿回路的数量


我们考虑到最后的哈密顿回路一定是有一段一段的树链串起来得到的

相当于每一棵树拆分成若干条树链,大小不限个数不限,所有的拼接起来,要求相邻的不来自同一颗树

我们可以树形 d p dp dp 求出一棵树分成 j j j 条链的方案数,这里带方向

接着,我们先不考虑环,只考虑序列,问题转换为有 m m m 种颜色的球,每种求有若干个,求最后拼接起来没有相邻颜色相同的个数

我们对每一种颜色钦定它至少有多少个相同要相邻来容斥,我们将相邻的合并后和其它颜色拼接
直接构造每一种颜色的 E G F EGF EGF,枚举合并过后的大小

F i ( x ) = ∑ j = 1 n f [ i ] [ j ] ∗ j ! ∑ k = 1 j ( j − 1 k − 1 ) ( − 1 ) j − k x k k ! F_i(x)=\sum_{j=1}^nf[i][j]*j!\sum_{k=1}^{j}\binom{j-1}{k-1}(-1)^{j-k}\frac{x^k}{k!} Fi(x)=j=1nf[i][j]j!k=1j(k1j1)(1)jkk!xk

考虑环的情况怎么做,第一个强行是 1 所在的链, 有 E G F EGF EGF
F 1 ( x ) = ∑ j = 1 n f [ i ] [ j ] ( j − 1 ) ! ∑ k = 1 j ( j − 1 k − 1 ) ( − 1 ) j − k x k − 1 ( k − 1 ) ! F_1(x)=\sum_{j=1}^nf[i][j](j-1)!\sum_{k=1}^j\binom{j-1}{k-1}(-1)^{j-k}\frac{x^{k-1}}{(k-1)!} F1(x)=j=1nf[i][j](j1)!k=1j(k1j1)(1)jk(k1)!xk1

减去不合法的情况,钦定最后一个在第一棵树上,那么要减去:
F 1 ′ ( x ) = ∑ j = 1 n f [ i ] [ j ] ( j − 1 ) ! ∑ k = 2 j ( j − 1 k − 1 ) ( − 1 ) j − k x k − 2 ( k − 2 ) ! F_1'(x)=\sum_{j=1}^nf[i][j](j-1)!\sum_{k=2}^j\binom{j-1}{k-1}(-1)^{j-k}\frac{x^{k-2}}{(k-2)!} F1(x)=j=1nf[i][j](j1)!k=2j(k1j1)(1)jk(k2)!xk2

暴力乘起来就可以了

树形DP,考虑 f [ i ] [ j ] [ 0 / 1 / 2 ] f[i][j][0/1/2] f[i][j][0/1/2] 表示到 i 选了 j 条链的方案数
0 表示 i i i 孤立, 1 1 1 表示 i 向下匹配, 2 2 2 表示有一条路径向上向下经过 i i i
这么做是为了处理 2 2 2 的系数

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 5e3 + 5;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int first[N], nxt[N << 1], to[N << 1], tot;
void adde(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y; }
cs int Mod = 998244353, inv2 = (Mod+1)>>1;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans = mul(ans, a); return ans; }
int fac[N], ifac[N]; int T, n, node;
int dp[N][N][3], sz[N];
int C(int n, int m){ if(n<0||m<0||n<m) return 0; return mul(fac[n], mul(ifac[m], ifac[n-m])); }
void prework(){
	fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
	for(int i = 2; i <= N-5; i++) fac[i] = mul(fac[i-1], i);
	ifac[N-5] = ksm(fac[N-5], Mod-2);
	for(int i = N-6; i >= 2; i--) ifac[i] = mul(ifac[i+1], i+1);
}
void Merge(int u, int v){
	int a = sz[u], b = sz[v]; sz[u] += sz[v];
	static int tp[N][3]; memset(tp, 0, sizeof(int) * (a+b+1) * 3);
	for(int i = 1; i <= b; i++){
		int all = add(dp[v][i][0], add(dp[v][i][1], dp[v][i][2]));
		for(int j = 1; j <= a; j++){
			Add(tp[i + j][0], mul(dp[u][j][0], all));
			Add(tp[i + j][1], mul(dp[u][j][1], all));
			Add(tp[i + j][2], mul(dp[u][j][2], all));
			Add(tp[i + j - 1][1], mul(dp[u][j][0], add(dp[v][i][1], mul(dp[v][i][0], 2))));
			Add(tp[i + j - 1][2], mul(dp[u][j][1], add(dp[v][i][0], mul(dp[v][i][1], inv2))));
		} 
	} memcpy(dp[u], tp, sizeof(int) * (a+b+1) * 3);
}
void dfs(int u, int fa){
	memset(dp[u], 0, sizeof(int) * (sz[u] + 1) * 3); sz[u] = dp[u][1][0] = 1; 
	for(int i = first[u]; i; i = nxt[i]) if(to[i]^fa) dfs(to[i], u), Merge(u, to[i]);
}
int ans[N], G[N];
void PolyMul(int a, int b){ 
	static int tmp[N + N];
	memset(tmp, 0, sizeof(int) * (a+b-1));
	for(int i = 0; i < a; i++) for(int j = 0; j < b; j++)
	Add(tmp[i + j], mul(ans[i], G[j]));
	memcpy(ans, tmp, sizeof(int) * (a+b-1));
}
int main(){
	T = read(); ans[0] = 1; prework();
	while(T--){
		int k = read(); tot = 0; memset(first, 0, sizeof(int) * (k+1));
		for(int i = 1; i < k; i++){ int x = read(), y = read(); adde(x, y); adde(y, x); }
		dfs(1, 0); static int ct[N];
		for(int i = 1; i <= k; i++)
		ct[i] = mul(T==0?fac[i-1]:fac[i], add(dp[1][i][0], add(dp[1][i][1], dp[1][i][2])));
		for(int i = 1; i <= k; i++){
			int coef = 0;
			for(int j = i; j <= k; j++){
				int vl = mul(ct[j], C(j-1, i-1));
				(j-i)&1 ? Add(coef, Mod-vl) : Add(coef, vl);
			} if(T == 0){
				G[i-1] = coef; if(i > 1) Add(G[i-2], Mod-coef);
			} else G[i] = coef;
		} if(T == 0) G[k] = 0; else G[0] = 0;
		for(int i = 0; i <= k; i++) Mul(G[i], ifac[i]);
		PolyMul(node+1, k+1); node += k;
	} int sum = 0;
	for(int i = 0; i <= node; i++) Add(sum, mul(fac[i], ans[i]));
	cout << sum; return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值