[ABC236Ex] Distinct Multiples 题解
前言
非常好容斥题,使我的笔旋转。
感觉这道题又让我学到了不少。
确实是没有想过可以把容斥系数都
思路
显然正着做不太好做,反着正好有事一堆
故我们考虑容斥。
首先最暴力的一个容斥就是我们对于每一个集合
复杂度
我们发现,其实我们本质上在乎的是这个连通块的分配情况,和它对应的容斥系数。
故我们定义
假设
故
观察到对于一个块的任意两个点其实都是可以连边的,故这个
定义
故
对于
简单理解一下,就是算出用偶数条边的总方案,减去那些不连通的。枚举
观察到对于
故我们简化一下式子。
同理:
但其实,我们更想知道的是
观察到边界值为
求一个简单的通项,
我们考虑把这个东西带回原来的
为了方便转移,我们定义
一个简单的转移就有了:
这个感觉是比较直观的,这里就不解释了。
初始值的话就是
终于是大功告成了,答案显然是
时间复杂度
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 998244353;
#define maxn 17
int n;
ll m, a[maxn];
ll Lcm[1 << 16], flg[1 << 16];
ll Gcd(ll x, ll y)
{
if(x > y) swap(x, y);
if(x == 0) return y;
return Gcd(x, y % x);
}
ll fact[maxn];
ll dp[1 << 16];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> a[i], Lcm[(1 << i - 1)] = a[i];
fact[0] = 1;
for (int i = 1; i <= n; ++i) fact[i] = fact[i - 1] * i % mod;
for (int i = 1; i < (1 << n); ++i)
{
if(__builtin_popcount(i) == 1) continue;
int j = __lg(i);
if(flg[i - (1 << j)])
{
flg[i] = true;
continue;
}
ll gcd = Gcd(Lcm[i - (1 << j)], a[j + 1]);
if(a[j + 1] * __int128(1) * Lcm[i - (1 << j)] / gcd > m) flg[i] = true;
else Lcm[i] = Lcm[i - (1 << j)] / gcd * a[j + 1];
}
dp[0] = 1;
for (int i = 1; i < (1 << n); ++i)
{
// cout << i << endl;
int I = i - (i & -i), k = i & -i;
for (int j = I; ; j = (j - 1) & I)
{
if(flg[j + k])
{
if(!j) break;
continue;
}
if(Lcm[j + k] > m)
{
if(!j) break;
continue;
}
dp[i] += dp[i - j - k] * ((m / Lcm[j + k]) % mod) % mod * fact[__builtin_popcount(j + k) - 1] % mod * ((__builtin_popcount(j + k) & 1) ? 1 : -1);
dp[i] %= mod, dp[i] += mod, dp[i] %= mod;
if(!j) break;
}
}
cout << dp[(1 << n) - 1] << endl;
return 0;
}
后记
人说,
林深时见鹿,
海蓝时见鲸,
梦醒时见你。
可实际,
林深时雾起,
海蓝时浪涌,
梦醒时也许未见鹿,
未见鲸,亦未见你。
但是,
鹿踏雾而来,
鲸随浪儿起,
你没回头,又怎知我没来过。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】