莫比乌斯反演
懵逼钨丝繁衍,你值得拥有
莫比乌斯反演是啥
若我们需要一个函数
,它非常的不好求
同时我们有一个函数
,它非常的好求
并且
与
之间的关系为:
或者
那么我们就可以得到 的计算式
或者是
其中 是莫比乌斯函数
定义为
可以在线性时间内处理出来(利用素数筛)
其实本质上这就是一个很巧妙的容斥
莫比乌斯反演有啥用
首先可以把不好求的
求出来
其次还是可以把不好求的
求出来
其实就是将求
的巨大时间复杂度转化为求
非常的舒服
给道例题练练手
BZOJ 1101 Zap
当 ,有多少组 满足
用
表示在
中
等于
的个数
用
表示在
中
是
的倍数的个数
显然我们会得到
这满足莫比乌斯函数的要求
于是 反演一下
得到 表达式
我们需要的是
那么转化为求
显然求
的时间复杂度为
鉴于询问有50000组,所以最坏情况时间复杂度为
挂掉
所以需要优化
转化一下原来的式子
当
,有多少组
满足
等价于
当
,有多少组
满足
这样相当于
求
式子非常的优美,其中
显然的,在求和过程中
是单调不增的,并且 最多都只有
种取值,那么分块求和即可
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 50010;
int t,a,b,d;
int mu[N],pri[N],sum[N],tot;
bool mark[N];
void get() {
mu[1]=1;
for(int i=2;i<=50000;++i) {
if(!mark[i]) {
pri[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot && pri[j]*i<=50000;++j) {
mark[i*pri[j]]=1;
if(i%pri[j]==0) break;
mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=50000;++i)
sum[i]=sum[i-1]+mu[i];
}
void work() {
scanf("%d%d%d",&a,&b,&d);
a/=d;b/=d;
if(a>b) swap(a,b);
int pos=0,ans=0;
for(int i=1;i<=a;i=pos+1) {
pos=min((a/(a/i)),(b/(b/i)));
ans+=(sum[pos]-sum[i-1])*(a/i)*(b/i);
}
printf("%d\n",ans);
}
int main() {
get();
scanf("%d",&t);
while(t--) work();
return 0;
}
其他好(水)题还有:
BZOJ 3930 选数
BZOJ 2154 Crash的数字表格
BZOJ 2301 Problem b
BZOJ 2820 YY的Gcd
BZOJ 2693 jzptab
加油吧,骚年