题解:P10461 多项式复合集合幂级数

题意

给定集合幂级数 \(F(x)\) 和一个 \(n\) 次多项式 \(G(x)\),对于每个 \(S\subseteq \{1,2,\cdots,n\}\),求 \([x^S]G(F(x))\)\(1\leq n\leq 20\)

题解

这个 DP 也是没有人类了。

显然有

\[[x^S]G(F(x))=\sum_{i=0}^ng_i[x^S]F(x)^i \]

考虑其组合意义,相当于对 \(S\) 做无序拆分,拆分出的每个集合 \(S_i\) 内部的方案数为 \([x^{S_i}]F(x)\),大小为 \(i\) 的拆分会乘上 \(g_ii!\) 的权值,求所有拆分的权值总和。

套路地给拆分钦定顺序,按照 \(\max(S)\) 从小到大加入集合。考虑 DP,比较正常的想法是,令 \(h_{i,j,S}\) 表示考虑到 \(\max(S)\leq i\)\(S\),拆分成 \(j\) 个部分的权值和。不难得出转移:

  • 不加入 \(\max(S)=i+1\) 的集合:\(h_{i,j,S}\to h_{i+1,j,S}\)
  • 加入 \(\max(S)=i+1\) 的集合:设 \([x^S]F_i(x)=[\max(S)=i][x^S]F(x)\),则 \(h_{i,j}\times F_{i+1}\to h_{i+1,j+1}\),其中乘法为子集卷积。

统计答案时,计算 \([x^S]G(F(x))=\sum\limits_{i=0}^n h_{n,i,S}g_ii!\) 即可。枚举到 \(i\) 时,需要做 \(i\) 次规模为 \(2^i\) 的子集卷积,时间复杂度为 \(\mathcal{O}(\sum_{i=1}^n 2^ii^3)=\mathcal{O}(2^nn^3)\),无法通过。

考虑一个极其智慧的处理,我们改变 \(j\) 的含义,将状态定义改为:令 \(h_{i,j,S}\) 表示考虑到 \(\max(S)\leq i\)\(S\)还需要加入 \(j\) 个部分的权值和。转移是类似的:

  • 不加入 \(\max(S)=i+1\) 的集合:\(h_{i,j,S}\to h_{i+1,j,S}\)
  • 加入 \(\max(S)=i+1\) 的集合:\(h_{i,j}\times F_{i+1}\to h_{i+1,j-1}\)

为什么要做这样的处理?我们发现改变状态定义后,会有 \(0\leq j\leq n-i\),因此枚举到 \(i\) 时,只需要做 \(\bm{n-i}\) 次规模为 \(2^i\) 的子集卷积,时间复杂度是 \(\mathcal{O}(\sum_{i=1}^n(n-i)2^ii^2)=\mathcal{O}(2^nn^2)\) 的!

时间复杂度的证明

\(f(n)=\sum\limits_{i=1}^n(n-i)2^ii^2\),则有

\[f(n)=f(n-1)+\sum_{i=1}^{n-1}2^ii^2=f(n-1)+\mathcal{O}(2^nn^2) \]

因此 \(f(n)=\mathcal{O}(2^nn^2)\)

但是改变状态后会有一个问题:如何统计答案?最终状态 \(h_{n,0,S}\) 所包含的拆分方案大小是不同的,貌似有点难办。其实,我们只需要在赋初值时令 \(h_{0,i,\varnothing}=g_ii!\) 就行了,这样对应大小的拆分就自然带上了正确的系数。这样 \(h_{n,0}\) 就是我们所求的集合幂级数了。

于是我们就 \(\mathcal{O}(2^nn^2)\) 地解决了此题。

代码
#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int, int>;
const int N = 1 << 20, MOD = 998244353;

template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chk_min(T &x, T y) { x = min(x, y); }
template<typename T> void chk_max(T &x, T y) { x = max(x, y); }
template<typename T> T add(T x, T y) { return x += y - MOD, x += x >> 31 & MOD; }
template<typename T> T sub(T x, T y) { return x -= y, x += x >> 31 & MOD; }
template<typename T> void cadd(T &x, T y) { x += y - MOD, x += x >> 31 & MOD; }
template<typename T> void csub(T &x, T y) { x -= y, x += x >> 31 & MOD; }

int n, f[N], g[21], pc[N];
int h[21][N], F[N][21];

void OR(auto &a, int n, int tp) {
	for (int k = 1; k < 1 << n; k <<= 1)
		for (int i = 0; i < 1 << n; i += k << 1) for (int j = 0; j < k; ++j) {
			int *A = a[i ^ j ^ k], *B = a[i ^ j];
			for (int p = 0; p <= n; ++p) (tp == 1 ? cadd<int> : csub<int>)(A[p], B[p]);
		}
}

void mul(int *a, int *res, int n) {
	static int b[N][21], tmp[21];
	for (int s = 0; s < 1 << n; ++s) fill(b[s], b[s] + n + 1, 0), b[s][pc[s]] = a[s];
	OR(b, n, 1);
	for (int s = 0; s < 1 << n; ++s) {
        fill(tmp, tmp + n + 1, 0);
        int *A = b[s], *B = F[s];
        for (int i = 0; i <= n; ++i) {
    		int val = 0;
    		for (int j = 0; j <= i; ++j) cadd<int>(val, (ll)A[j] * B[i - j] % MOD);
    		tmp[i] = val;
    	}
        copy(tmp, tmp + n + 1, b[s]);
    }
	OR(b, n, -1);
	for (int s = 0; s < 1 << n; ++s) cadd(res[s], b[s][pc[s]]);
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n;
	for (int s = 1; s < 1 << n; ++s) pc[s] = pc[s ^ lowbit(s)] + 1;
	for (int s = 0; s < 1 << n; ++s) cin >> f[s];
	for (int i = 0; i <= n; ++i) cin >> g[i];
	for (int i = 0, fc = 1; i <= n; ++i)
		h[i][0] = (ll)g[i] * fc % MOD, fc = (ll)fc * (i + 1) % MOD;
	for (int i = 0; i < n; ++i) {
		for (int s = 0; s < 1 << i; ++s) F[s][pc[s]] = f[s ^ (1 << i)];
		OR(F, i, 1);
		for (int j = 1; j <= n - i; ++j) mul(h[j], h[j - 1] + (1 << i), i);
		for (int s = 0; s < 1 << i; ++s) fill(F[s], F[s] + i + 1, 0);
	}
	for (int s = 0; s < 1 << n; ++s) cout << h[0][s] << ' ';
	return 0;
}
posted @ 2026-01-14 21:16  P2441M  阅读(2)  评论(0)    收藏  举报