子集反演
定义
所谓子集反演,其实是定义在子集关系上的反演。先给出常用形式:
设 \(f_S\) 为限制为恰好是集合 \(S\) 的答案,\(g_S\) 为限制为 \(S\) 的子集的答案,可以得到关系式 \(g_S=\sum_{T\subseteq S}f_T\),子集反演给出 \(f_S=\sum_{T\subseteq S}(-1)^{|S|-|T|}g_T\)。
对称地,\(f_S\) 定义不变,\(g_S\) 为限制为 \(S\) 的超集的答案,可以得到关系式 \(g_S=\sum_{S\subseteq T}f_T\),子集反演给出 \(f_S=\sum_{S\subseteq T}(-1)^{|T|-|S|}g_T\)。
P3349
给你一张 \(n\) 个点 \(m\) 条边的无向图,和一棵 \(n\) 个点的树,问有多少种将树上点到图上点双射的方案使得双射后的树为图的一棵生成树。
\(n\leq 17\)。
不难想到暴力状压 \(f_{u,i,S}\) 为 \(u\) 子树内双射到了 \(S\) 中的点,\(u\) 映射为 \(i\) 的方案数。转移形式是子集卷积,可以做到 \(O(n^42^n)\) 过不去。
考虑容斥掉“双射”这个条件。也就是说设 \(f_{u,i,S}\) 为 \(u\) 子树内满射到了 \(S\) 中的点,\(u\) 映射为 \(i\) 的方案数;\(g_{u,i,S}\) 为 \(u\) 子树内映射到了 \(S\) 中一个子集,\(u\) 映射为 \(i\) 的方案数。可以得到关系式 \(g_{u,i,S}=\sum_{T\subseteq S}f_{u,i,T}\),可以使用子集反演。因为定义域与值域的集合大小相等时,满射就是双射,所以答案即为 \(f_{u,i,U}\)。
所以先枚举所有的 \(S\),然后对着这个 \(S\) 算出 \(g_{u,i}\)。对于一个 \(S\) 计算 \(g\) 的复杂度是 \(O(n^3)\),所以总复杂度 \(O(n^32^n)\)。
P4336
无向图每条边有一个颜色,求恰好有 \(n-1\) 种颜色的生成树个数。
\(n\leq 17\)。
生成树个数计数考虑矩阵树定理,但是会算重。
仿照上面那题设 \(f_S\) 为恰好使用 \(S\) 内所有颜色方案数,\(g_S\) 为使用 \(S\) 的一个子集的方案数,可以直接子集反演。\(g_S\) 的计算可以直接使用矩阵树定理。
\(O(n^32^n)\)。
P11714
发现强连通很难刻画,不妨使用单步容斥,转化成算非强连通方案数。
考虑刻画一下非强连通,发现缩点之后一定是一个点数大于 \(1\) 的 DAG。
于是,呃,考虑一种比较劣的做法,就是先暴力枚举这个图缩点之后每个点属于的 SCC 是哪个,然后对着这个算方案数。
于是现在需要算 DAG 生成子图。
考虑刻画 DAG,发现其一定存在一些点入度为 \(0\)。把这些点删了之后剩下的点还是 DAG,于是找到了一个子结构,可以对着这个 DP。
设 \(dp_S\) 表示 \(S\) 的答案,\(E_{S,T}\) 表示 \(S\) 到 \(T\) 的边数,则有:
但是这对吗?这不对。因为 \(S-T\) 里面可能还有入度为 \(0\) 的点,会导致算重。
考虑一下子集反演,当前全集为 \(S\),设 \(f_T\) 表示 \(T\) 内点恰为所有入度为 \(0\) 的点的方案数,\(g_T\) 表示钦定 \(T\) 内点入度为 \(0\) 的方案数。显然 \(g_T=dp_{S-T}2^{E_{T,S-T}}\)。有关系式:
反演得到:
重写一下转移式,进行一些式子的推导:
于是得到了一个 \(O(3^n)\) 算一次的做法,但是乘上暴力枚举缩点方案数之后复杂度爆炸。
考虑拓展这个东西到非强连通图计数上。
设 \(dp_S\) 表示 \(S\) 的答案,即 \(S\) 缩成一个点的方案数。仿照上面的方法,设 \(g_T\) 表示 \(T\) 内的点缩成若干个入度为 \(0\) 的点的方案数,转移的时候枚举 \(T\),钦定 \(T\) 内点缩成了若干入度为 \(0\) 的点:
但是发现容斥系数是不确定的,因为并不知道 \(T\) 内的点到底缩成了多少个点。于是考虑修改 \(g_T\) 的定义,令其包含上容斥系数,即缩成奇数个点的方案数减去缩成偶数个点的方案数,再写:
这个式子可以 \(O(3^n)\) 做了。但是还没做完,还需要算 \(g_S\)。
考虑找子结构,发现这些缩完之后的点互不相干,于是可以钦定 \(\operatorname{lowbit}(S)\) 所属的 SCC 是新加入的:
第一项是考虑 \(S\) 单独为一个 SCC,\(1\) 是奇数,所以要加上。后面一串是增加一个 SCC,奇偶性改变,所以要减去。
但是又出现了新问题,\(g_S\) 和 \(dp_S\) 好像互相依赖。真的互相依赖吗?\(dp_S\) 的转移式中只有 \(S=T\) 时会用到 \(g_S\)。重申一遍 \(dp_S\) 是 \(S\) 强连通的方案数,那么在 \(dp_S\) 中减去 \(g_S\) 中的 \(dp_S\) 时,减去的这个 \(dp_S\) 是合法的方案数,其不应该被减掉。
所以先算出 \(g_S\),不加上 \(dp_S\),再算出来 \(dp_S\),把 \(dp_S\) 加到 \(g_S\) 上,是正确的。
于是现在只剩下最后一个问题了,怎么算 \(E_{S,T}\)。
看起来这个状态数是 \(4^n\) 的,但是观察发现对于每个 \(S\),只需要用到形如 \(E_{T,S-T}\) 或者 \(E_{S,S}\) 形式的 \(E\)。\(T\subseteq S\),所以状态数是 \(3^n\)。但是空间复杂度还是 \(O(4^n)\),所以考虑每枚举一个 \(S\) 就重新计算 \(E_{T,S-T}\),对于 \(E_{T,T}\) 另外开一个计算。
先预处理出 \(out_u\) 表示 \(u\) 连向的点集,\(in_u\) 表示连向 \(u\) 的点集,这样就有递推式:
这样,整个问题就能做到 \(O(3^n)\) 了,非常厉害。

浙公网安备 33010602011771号