当前位置: 首页 > news >正文

快速莫比乌斯变换(FMT)与莫比乌斯反演 例题:树上lcm

快速莫比乌斯变换

数学公式

  • SSS为全集,TTT为其子集
    zeta变换:F(S)=∑T⊆Sf(T)莫比乌斯反演:f(S)=∑T⊆S(−1)∣S∣−∣T∣F(T)\begin{align} &zeta变换:F(S)=\sum_{T\subseteq S}f(T)\\ \\ &莫比乌斯反演:f(S)=\sum_{T\subseteq S}(-1)^{|S|-|T|}F(T) \end{align} zeta变换:F(S)=TSf(T)莫比乌斯反演:f(S)=TS(1)STF(T)
  • zetazetazeta变换与sosdpsosdpsosdp中的子集求和、超集求和一致,只不过这里的集合范畴不仅限于二进制集合
  • 莫比乌斯反演的作用就在于将超集和或者子集和反演为原来的函数

代码实现(二进制集合)

zetazetazeta变换(子集和)

伪代码:

for 每一位 bit i:for 每个 mask:如果 mask 有第 i 位:F[mask] += F[mask 去掉第 i 位]

代码:

void zeta(vector<ll> &f, int n) {for (int i = 0; i < n; i++) { // 枚举每一位for (int mask = 0; mask < (1 << n); mask++) {if (mask & (1 << i)) { // 如果第 i 位是 1f[mask] += f[mask ^ (1 << i)];}}}
}

MobiusMobiusMobius反演

伪代码:

for 每一位 bit i:for 每个 mask:如果 mask 有第 i 位:F[mask] -= F[mask 去掉第 i 位]

代码:

void mobius(vector<ll> &f, int n) {for (int i = 0; i < n; i++) {for (int mask = 0; mask < (1 << n); mask++) {if (mask & (1 << i)) {f[mask] -= f[mask ^ (1 << i)];}}}
}

数论中的莫比乌斯反演

若有:
F(n)=∑d∣nf(d)F(n)=\sum_{d|n}f(d) F(n)=dnf(d)
其中d∣nd|ndn表示dddnnn的因数
则有:
f(n)=∑d∣nμ(d)⋅F(nd)f(n)=\sum_{d|n}\mu(d)·F\left( \frac{n}{d} \right) f(n)=dnμ(d)F(dn)
其中μ(d)\mu(d)μ(d)为数论莫比乌斯函数,对应着莫比乌斯反演中的(−1)∣S∣−∣T∣(-1)^{|S|-|T|}(1)STnnn对应着全集SSSnd\frac{n}{d}dn对应着子集TTT

莫比乌斯函数的定义:
μ(n)={1,n=1(−1)kn是k个不同质数的乘积0n有平方因子\mu(n)= \begin{cases} \begin{align} &1,&n=1\\ \\ &(-1)^k &n是k个不同质数的乘积\\ \\ &0 &n有平方因子 \end{align} \end{cases} μ(n)=1,(1)k0n=1nk个不同质数的乘积n有平方因子

基于欧拉筛的μ\muμ函数代码:

int mu[MX], vis[MX], p[MX], cnt;
void init() {mu[1] = 1;rep(i, 2, MX - 1) {if (!vis[i])p[++cnt] = i, mu[i] = -1;for (int j = 1; i * p[j] < MX; j++) {vis[i * p[j]] = 1;if (i % p[j] == 0)break;mu[i * p[j]] = -mu[i];}}
}

例题:树上lcm

image

思路

f(x)f(x)f(x)为路径lcmlcmlcmxxx的简单路径数
由于涉及因数与求和,联想到莫比乌斯反演
设f(x)=∑d∣xμ(d)g(xd)设f(x)=\sum_{d|x}\mu(d)g\left( \frac{x}{d} \right) f(x)=dxμ(d)g(dx)
由莫比乌斯反演:
g(n)=∑d∣nf(d)g(n)=\sum_{d|n}f(d) g(n)=dnf(d)
因此得到g(n)g(n)g(n)的定义:路径lcmlcmlcmnnn的因数的简单路径数

因此可以先将整棵树上不是xxx的因数的节点删去,此时每个连通块内的任意两点构成的简单路径的lcmlcmlcm都是xxx的因数!

dfs染色统计每个连通块的大小,计算有多少条简单路径即可

代码实现
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cmath>
#include<unordered_map>
#include<set>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i ++)
#define per(i, a, b) for(ll i = (a); i >= (b); i --)
//#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
#define int ll
const int N = 1e5 + 5;
const int MX = 1e7 + 5;
int n, x;
struct node {vector<int>e;int val, tag, siz;
}a[N];ll gcd(ll a, ll b) {if (b == 0)return a;return gcd(b, a % b);
}
ll lcm(ll a, ll b) {return a * b / gcd(a, b);
}void dfs(int u, int fa, int fac) {if (lcm(a[u].val, fac) > fac)a[u].tag = -1;else a[u].tag = 0;for (auto& son : a[u].e) {if (son == fa)continue;dfs(son, u, fac);}
}void dfs2(int u, int fa) {a[u].tag = 1; a[u].siz = 1;for (auto& son : a[u].e) {if (son == fa || a[son].tag != 0)continue;dfs2(son, u);a[u].siz += a[son].siz;}
}int mu[MX], vis[MX], p[MX], cnt;
void init() {mu[1] = 1;rep(i, 2, MX - 1) {if (!vis[i])p[++cnt] = i, mu[i] = -1;for (int j = 1; i * p[j] < MX; j++) {vis[i * p[j]] = 1;if (i % p[j] == 0)break;mu[i * p[j]] = -mu[i];}}
}void eachT() {set<int>fac;rep(i, 1, n)a[i].e.clear(), a[i].tag = 0,a[i].siz=1;cin >> n >> x;rep(i, 1, n - 1) {int u, v; cin >> u >> v;a[u].e.push_back(v), a[v].e.push_back(u);}rep(i, 1, n)cin >> a[i].val;for (int i = 1; i * i <= x; i++) {if (x % i == 0)fac.insert(i), fac.insert(x / i);}ll ans = 0;for (auto& f : fac) {ll now = 0;dfs(1, 0, f);rep(i, 1, n) {if (a[i].tag == 0) {dfs2(i, 0);int s = a[i].siz;now += s * (s - 1) / 2 + s;}}ans += now * mu[x / f];}cout << ans << '\n';
}
signed main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);init();ll t = 1;cin >> t;while (t--) { eachT(); }
}
http://www.xdnf.cn/news/1252423.html

相关文章:

  • SELinux 安全机制详解与管理
  • 组合期权:跨式策略
  • 批量提问程序开发方案:基于Python的百度文小言接口实现
  • 基于 Jenkins Pipeline 实现 DITA 文档自动化构建与发布(开源方案)
  • 百度智能云给“数字人”发工牌
  • Boosting 知识点整理:调参技巧、可解释性工具与实战案例
  • Bug 记录:SecureRandom.getInstanceStrong()导致验证码获取阻塞
  • 【motion】标签体系设计与检索 1:HumanML3D 和 KIT Motion-Language(KITML)
  • Java 使用动态代理和反射实现字段变更跟踪
  • 生成网站sitemap.xml地图教程
  • 【STM32U385RG 测评】基于VSCode的STM32开发环境搭建
  • 西门子PLC基础指令6:读取时钟指令、设置时钟指令、使能含义与注意
  • 【32】C++实战篇—— m行n列的坐标点,求每行相邻点X差值dX,每列相邻点y差值dY,并以矩阵形式左端对齐
  • JAVA--流程控制语句
  • 【VS + Qt】VS2022 Qt 开发中 ui_xx.h 文件编辑报错但编译正常的问题解决
  • 「iOS」————单例与代理
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘caffe’问题
  • 河南萌新联赛2025第四场-河南大学
  • K8S云原生监控方案Prometheus+grafana
  • yolov1-v3原理解析
  • DHCP 服务器与DNS服务器
  • 服务器——“查询不到显卡驱动,且输入nvidia-smi报错”的解决办法
  • 2.6 sync
  • 媒体资产管理系统和OCR文字识别的结合
  • 多端同步新解法:Joplin+cpolar联合通过开源设计实现跨平台无缝协作?
  • 自动驾驶系统的网络安全风险分析
  • 013 HTTP篇
  • Transwell 细胞迁移与侵袭实验:从原理到操作的详细指南
  • Hive【应用 04】常用DDL操作(数据库操作+创建表+修改表+清空删除表+其他命令)
  • 【android bluetooth 协议分析 03】【蓝牙扫描详解 4】【BR/EDR扫描到设备后如何上报给app侧】