[ABC236Ex] Distinct Multiples (集合划分容斥)

Solution

关键限制是 2.AiAj
这也是上午模拟赛 T3 导致我暴力不会的东西
考虑更一般的,连边 (i,j),表示 ai=aj 的限制,那么本题考虑这样的一个完全图
那么枚举选哪些边,记为集合 S,于是答案就是 S(1)|S|f(S)
f(S) 此时的计算就面临一些连通块,连通块内要求 a 相等,这是容易计算的
但容斥本身是 O(2m),考虑优化

因为枚举选的边集,很多 f(S) 是相同的,因为计算只取决于各个连通块的情况,而完全图一些边的差异不会影响各个连通块的连通关系,那么优化 f(S) 的定义,转向只考虑点集,f(S) 表示将点集 S 划分成若干个连通的答案
那么 f(S) 就是各个连通块方案数带容斥系数的和

考虑一个连通块的答案 g(S),一开始连通块的限制也是个完全图
那么继续枚举边集,要求选了的边集能使点集 S 连通
g(S)=E(1)|E|F(S)F(S) 表示连通块 S 内要求 a 都相同的答案,这是容易计算的
记容斥系数和为 h(S),算 h(S) 的话连通约束较困难,再容斥计算,记 n=|S|
不考虑连通,那么直接枚举选了几条边即可,可以看出是个二项式定理,那么有答案为 [n=1]
再减掉不连通的情况,此时 S 被分成若干连通块,为避免考虑连通块时算重,枚举代表元所在连通块大小
那么 h(n)=[n=1]i=1n1(n1i1)h(i)[ni=1],得到 h(n)=(1)n1(n1)!
原来这是一个经典的容斥模型,即集合划分容斥。

本题 f(S) 的转移是枚举子集的,但仍会算重,同样枚举代表元所在连通块

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 17, P = 998244353;
int n, h[N], f[1 << N], g[1 << N], num[1 << N];
LL m, d[N];
void Add(int &x, int y){((x += y) >= P) && (x -= P);}
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] = (P - (LL)(i - 1) * h[i - 1] % P) % P;
int all = (1 << n) - 1;
for(int i = 1; i <= all; i++) {
for(int j = i; j; j -= j & -j) ++num[i];
LL lcm = 1;
for(int j = 0; j < n; j++) if (i >> j & 1) {
lcm /= __gcd(lcm, d[j]);
if (lcm > m / d[j]) {lcm = m + 1; break;}
lcm *= d[j];
}
g[i] = m / lcm % P;
}
f[0] = 1;
for(int s = 1; s <= all; s++) {
int now = __builtin_ctz(s);
for(int t = s; t; t = (t - 1) & s) if (t >> now & 1)
Add(f[s], (LL)g[t] * h[num[t]] % P * f[s ^ t] % P);
}
printf("%d\n", f[all]);
}
posted @   leiyuanze  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示