ABC236Ex

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

令全集 E={(i,j)|1i<jN,i,j}

对于 SE,令 f(S) 表示以下的值:

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

那么由容斥得答案即为 SEf(S)(1)|S|

但是边的数量是 N2 级别的,直接做显然不行。

对于一个点集 T,令 g(T)=MlcmiTDi。令 h(n) 表示令 n 个点连通的边集的容斥系数的和。即对于 n×(n1)2 条边的子集,若它能令 n 个点连通,则如果边集大小为奇数则将 h(n) 减去 1,否则加上 1

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

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

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

那么对于 f(S)(1)|S|S 将原图分成了若干个连通块,假设每个连通块的点集分别为 T1,T2,,Tk,那么即为 ki=1g(Tk)h(|Tk|)

dpT 表示将点集 T 划分成若干个连通块的答案,那么最终答案就是 dp{1,2,,n}

假设固定住 T 中的某个节点 u,那么转移为 dpT=TT,uTg(T)h(|T|)×dpTT,初值为 dp=1

时间复杂度 O(3n)

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 @   Kobe303  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示