ABC236Ex

首先 \(1,3\) 条件很好满足,但是 \(2\) 有点麻烦,考虑容斥掉条件 \(2\)

令全集 \(E=\left\{(i,j)|1\le i\lt j\le N,i\inℤ,j\inℤ\right\}\)

对于 \(S\subseteq E\),令 \(f(S)\) 表示以下的值:

考虑一张有 \(N\) 个点的无向图,如果 \((i,j)\in S\),那么 \(i,j\) 之间有一条边。对于图里的每一个连通块,连通块里的点的值都应该相等,那么整张图的确定点值的方案数就是 \(f(S)\)

那么由容斥得答案即为 \(\sum\limits_{S\subseteq E}f(S)(-1)^{\left|S\right|}\)

但是边的数量是 \(N^2\) 级别的,直接做显然不行。

对于一个点集 \(T\),令 $g(T)=\left \lfloor \dfrac{M}{\text{lcm}_{i\in T}D_i} \right \rfloor $。令 \(h(n)\) 表示令 \(n\) 个点连通的边集的容斥系数的和。即对于 \(\dfrac{n\times(n-1)}{2}\) 条边的子集,若它能令 \(n\) 个点连通,则如果边集大小为奇数则将 \(h(n)\) 减去 \(1\),否则加上 \(1\)

\(g(T)\) 可以 \(\mathcal O(2^N)\) 预处理出来,接下来讲讲怎么求出 \(h\)

首先 \(h(1)=1\),这很显然,接着对于 \(n\ge2,h(n)=-(n-1)h(n-1)\),证明:

考虑用总的减去不连通的容斥系数。显然,因为总的方案是 \(2^{\frac{n\times (n-1)}{2}}\),奇偶各占一半,相互抵消。同理,考虑对于节点 \(1\) 所在连通块,如果其大小不为 \(n-1\),因为剩下的点可以随便连边,同样奇偶各占一半,相互抵消,所以只用考虑其大小为 \(n-1\),那么枚举剩下一个孤立点是什么,即 \(h(n)=0-(n-1)h(n-1)=-(n-1)h(n-1)\)

那么对于 \(\sum f(S)(-1)^{\left|S\right|}\)\(S\) 将原图分成了若干个连通块,假设每个连通块的点集分别为 \(T_1,T_2,\cdots,T_k\),那么即为 \(\prod\limits_{i=1}^k g(T_k)h(\left|T_k\right|)\)

\(dp_T\) 表示将点集 \(T\) 划分成若干个连通块的答案,那么最终答案就是 \(dp_{\left\{1,2,\cdots,n\right\}}\)

假设固定住 \(T\) 中的某个节点 \(u\),那么转移为 \(dp_{T}=\sum\limits_{T^{′ }\subset T,u\in T^{′ }}g(T^{′ })h(\left|T^{′ }\right|)\times dp_{T\setminus T^{′ }}\),初值为 \(dp_{\emptyset}=1\)

时间复杂度 \(\mathcal O(3^n)\)

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 16, mod = 998244353;
int n; ll m;
ll d[N];
int f[1<<N], g[1<<N], h[N+5];

void add(int &a, int b) {
   a += b;
   if (a >= mod) a -= mod;
}

int main() {
   scanf("%d%lld", &n, &m);
   for (int i = 0; i < n; ++i) scanf("%lld", &d[i]);
   h[1] = 1;
   for (int i = 2; i <= n; ++i) {
   	h[i] = -1ll * (i - 1) * h[i - 1] % mod;
   	if (h[i] < 0) h[i] += mod;
   }
   for (int S = 1; S < (1 << n); ++S) {
   	ll lcm = 1;
   	for (int i = 0; i < n; ++i) if (S >> i & 1) {
   		lcm /= __gcd(lcm, d[i]);
   		if (lcm > m / d[i]) {
   			lcm = m + 1;
   			break;
   		}
   		lcm = lcm * d[i];
   	}
   	g[S] = (m / lcm) % mod * h[__builtin_popcount(S)] % mod;
   }
   f[0] = 1;
   for (int S = 1; S < (1 << n); ++S) {
   	int mn = __builtin_ctz(S);
   	for (int _S = S; _S > 0; _S = (_S - 1) & S) if (_S >> mn & 1)
   		add(f[S], 1ll * f[S ^ _S] * g[_S] % mod);
   }
   printf("%d", f[(1 << n) - 1]);
   return 0;
} 
posted @ 2022-10-22 20:12  Kobe303  阅读(21)  评论(0编辑  收藏  举报