• zr1505


    k = 1

    (S(c) = {c_xmid xin S }), 那么答案就是:

    [sum_c|S(c)| = sum_c sum_{i=1}^m[i in S(c)] ]

    交换求和符号:

    [sum_{i=1}^msum_c [iin S(c)] ]

    这个意义就是枚举颜色 i, 然后求有多少种染色方案, 使得 i 在 (S(c)) 中, 设为 G(i)。

    然后显然每种颜色是等价的, 那么 G(1) = G(2) = ... = G(m)。

    所以只需要求 G(1), 取个补变成求有几种染色方案使得 1 不在 S(c) 中。

    这个比较好算。


    标算

    相当于求 (sum_c|S(c)|^k)

    根据斯特林展开公式:

    [color{teal}{x^k = sum_{i=0}^k {krace i}x^{underline i} = sum_{i=0}^k {krace i}inom xi i!} ]

    代入得到:

    [color{orange}{sum_c} sum_{i=0}^k{krace i}color{orange}{inom {|S(c)|}i}i!\ sum_{i=0}^k{krace i}i! color{orange}{sum_cinom {|S(c)|}i} ]

    剩下的就是求 (g(i) = sum_c inom{|S(c)|}i), 等价于:枚举所有大小为 i 的颜色集合 t, 并算出有多少 c 满足 (t subseteq S(c))

    令 h(i) 为:对于某个大小为 i 的颜色集合 T, 有几个 c 满足 (t subseteq S(c))

    那么 (g(i) = inom mi h(i))

    对于 (h(i)), 考虑用容斥原理去算, 每次枚举有 j 个 T 中的颜色不在 S(c) 中, 令 dp(j) 表示对于一个大小为 j 的颜色集合 Ban, 有几个 c 满足 (Bancap S(c) = varnothing), 有:

    [h(i) = sum_{j=0}^i (-1)^jinom ijdp(j) ]

    对于 dp(j), 可以直接树形 dp。

    那么算法的流程就是:

    对于 0...k,预处理出 dp(j)。

    然后计算即可。

    总复杂度 (O(nk+k^2))

    #include <bits/stdc++.h>
    typedef long long LL;
    using namespace std;
    
    const int N = 1e5 + 23, K = 93, mo = 998244353;
    
    int n, m, k, _S_, S[N];
    bool vis[N];
    LL c[K][K], s[K][K];
    
    int ecnt, hd[N], nt[N*2+1], vr[N*2+1];
    void ad (int x, int y) { nt[++ ecnt] = hd[x], hd[x] = ecnt, vr[ecnt] = y; }
    
    LL f[N][2], dp[K];
    void dfs (int x, int F, int j) {
    	f[x][0] = 1ll, f[x][1] = vis[x] ? 0ll : 1ll;
    	for (int i = hd[x]; i; i = nt[i]) {
    		int y = vr[i];
    		if (y == F) continue;
    		dfs (y, x, j);
    		f[x][0] *= ((LL)(m - j - 1) * f[y][0] % mo + (LL)j * f[y][1] % mo) % mo, f[x][0] %= mo;
    		f[x][1] *= ((LL)(m - j) * f[y][0] % mo + (LL)(j - 1) * f[y][1] % mo) % mo, f[x][1] %= mo;
    	}
    }
    
    int main()
    {
    	scanf ("%d%d%d%d", & n, & m, & k, & _S_);
    	for (int i = 1; i <= _S_; ++ i) scanf ("%d", & S[i]), vis[S[i]] = true;
    	for (int i = 1, x, y; i < n; ++ i) scanf("%d%d", & x, & y), ad (x, y), ad (y, x);
    	c[0][0] = s[0][0] = 1ll;
    	for (int i = 1; i <= k; ++ i) {
    		c[i][0] = 1ll;
    		for (int j = 1; j <= i; ++ j) {
    			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mo;
    			s[i][j] = (s[i - 1][j] * (LL)j + s[i - 1][j - 1] ) % mo;
    		}
    	}
    	for (int i = 0; i <= k; ++ i)
    		dfs (1, 0, i), dp[i] = ((LL)(m - i) * f[1][0] % mo + (LL)i * f[1][1] % mo) % mo;
    	LL ans = 0ll, faci = 1ll;
    	for (int i = 0; i <= k; ++ i) {
    		LL now = 0ll;
    		for (int j = 0; j <= i; ++ j)
    			now = (now + (j&1 ? -1ll : 1ll) * c[i][j] * dp[j] % mo) % mo;
    		ans += now * faci % mo * s[k][i] % mo;
    		ans %= mo;
    		faci = faci * (LL)(m - i) % mo;
    	}
    	cout << (ans % mo + mo) % mo;
    	return 0;
    }
    
  • 相关阅读:
    Linux内核学习笔记七——内核同步机制和实现方式
    Linux内核学习笔记五——中断推后处理机制
    Linux内核学习笔记十——虚拟文件系统概念
    Android中LocalSocket使用
    Linux下常见命令
    Linux内核学习笔记八——定时器和时间管理
    Linux内核学习笔记九——内核内存管理方式
    [Android]Android的常用adb命令
    Linux内核学习笔记六——并发和同步概念
    Linux内核学习笔记十一——I/O层和I/O调度机制
  • 原文地址:https://www.cnblogs.com/tztqwq/p/14490782.html
Copyright © 2020-2023  润新知