【51nod】1222 最小公倍数计数 莫比乌斯反演+组合计数

【题意】给定a和b,求满足a<=lcm(x,y)<=b && x<y的数对(x,y)个数。a,b<=10^11。

【算法】莫比乌斯反演+组合计数

【题解】★具体推导过程参考51nod1222 最小公倍数计数

过程运用到的技巧:

1.将所有i和j的已知因子提取出来压缩上届。

2.将带有μ(k)的k提到最前面,从而后面变成单纯的三元组形式。

最终形式:

$$ans=\sum_{k=1}^{\sqrt n} \mu(k)  \sum_{d}    \sum_{i} \sum_{j} [i*j*d<=\frac{n}{k^2}]$$

问题转化为枚举(d,i,j)三元组满足其乘积<=n/k^2。

虽然题目要求组合(有序),但是有d的存在,所以先求排列(无序)。

但是三元组的排列不方便统计,所以求三元组的组合乘上排列系数

先算严格从小到大的,然后减去两个相同的和三个相同的。

最后+n后/2就变成组合。

听说复杂度O(n^(2/3))。

#include<cstdio>
#include<cmath>
#define int long long
using namespace std;
const int maxn=1000010;
int miu[maxn],prime[maxn],tot;
bool vis[maxn];
int solve(int x){
    if(!x)return 0;
    int N=(int)sqrt(x)+1,ans=0;
    for(int k=1;k<=N;k++)if(miu[k]){
        int n=x/(k*k);
        for(int d=1;d*d*d<=n;d++){
            for(int i=d+1;i*i*d<=n;i++)ans+=miu[k]*((n/(d*i)-i)*6+3);
            ans+=miu[k]*((n/(d*d)-d)*3+1);
        }
    }
    return (ans+x)/2;
}    
#undef int
int main(){
#define int long long
    int A,B;
    scanf("%lld%lld",&A,&B);
    int N=(int)sqrt(B)+1;
    miu[1]=1;
    for(int i=2;i<=N;i++){
        if(!vis[i]){miu[prime[++tot]=i]=-1;}
        for(int j=1;j<=tot&&i*prime[j]<=N;j++){
            vis[i*prime[j]]=1;//
            if(i%prime[j]==0)break;
            miu[i*prime[j]]=-miu[i];
        }
    }
    printf("%lld\n",solve(B)-solve(A-1));
    return 0;
}
View Code

 

posted @ 2018-03-01 21:19  ONION_CYC  阅读(272)  评论(0编辑  收藏  举报