Codeforces900 D. Unusual Sequences(莫比乌斯反演)

题意:

给定x,y,问有多少种序列a,
满足a[i]>=1,gcd(a[])=x,且sum(a[])=y.

数据范围:x,y<=1e9

解法:

y % x ! = 0 时 肯 定 无 解 问 题 可 以 转 换 为 满 足 和 为 k = y / x , g c d = 1 的 序 列 数 量 设 g ( k ) 为 满 足 和 为 k 的 方 案 数 因 为 序 列 的 长 度 可 以 不 定 , 可 以 看 作 是 k 个 1 , 在 k − 1 个 空 位 插 入 0 到 k − 1 个 隔 板 因 此 g ( k ) = ∑ i = 0 k − 1 C ( k − 1 , i ) = 2 k − 1 g ( k ) 没 有 考 虑 g c d , 里 面 包 含 g c d = 1 到 g c d = k 的 所 有 方 案 . 设 f ( x ) 为 g c d = 1 , 和 为 y / x 的 方 案 数 , 因 为 对 于 g ( k ) , 所 有 可 能 的 序 列 的 g c d 一 定 是 k 的 约 数 因 此 g ( k ) = ∑ d ∣ k f ( k d ) . 为 什 么 是 f ( k d ) 呢 ? f ( k d ) 的 和 是 k d 不 是 k 呀 ? 这 是 因 为 和 为 k d , g c d = 1 的 序 列 , 每 个 数 都 乘 上 d 之 后 就 变 成 和 为 k , g c d = d 的 序 列 了 反 演 得 : f ( k ) = ∑ d ∣ k μ ( d ) g ( k d ) O ( s q ( k ) ) 枚 举 因 子 d 即 可 . 预 处 理 质 数 , 然 后 μ ( d ) 就 可 以 O ( s q ( d ) / l o g ( d ) ) 计 算 . y\%x!=0时肯定无解\\ 问题可以转换为满足和为k=y/x,gcd=1的序列数量\\ 设g(k)为满足和为k的方案数\\ 因为序列的长度可以不定,可以看作是k个1,在k-1个空位插入0到k-1个隔板\\ 因此g(k)=\sum_{i=0}^{k-1}C(k-1,i)=2^{k-1}\\ g(k)没有考虑gcd,里面包含gcd=1到gcd=k的所有方案.\\ 设f(x)为gcd=1,和为y/x的方案数,\\ 因为对于g(k),所有可能的序列的gcd一定是k的约数\\ 因此g(k)=\sum_{d|k}f(\frac{k}{d}).\\ 为什么是f(\frac{k}{d})呢?f(\frac{k}{d})的和是\frac{k}{d}不是k呀?\\ 这是因为和为\frac{k}{d},gcd=1的序列,每个数都乘上d之后\\ 就变成和为k,gcd=d的序列了\\ 反演得:f(k)=\sum_{d|k}\mu(d)g(\frac{k}{d})\\ O(sq(k))枚举因子d即可.\\ 预处理质数,然后\mu(d)就可以O(sq(d)/log(d))计算. y%x!=0k=y/x,gcd=1g(k)k,k1,k10k1g(k)=i=0k1C(k1,i)=2k1g(k)gcd,gcd=1gcd=k.f(x)gcd=1,y/x,g(k),gcdkg(k)=dkf(dk).f(dk)?f(dk)dkkdk,gcd=1,dkgcd=d:f(k)=dkμ(d)g(dk)O(sq(k))d.μ(d)O(sq(d)/log(d)).

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e6+5;
const int mod=1e9+7;
int notprime[maxm];
int prime[maxm],cnt;
int x,y;
int ppow(int a,int b,int mod){
    int ans=1%mod;a%=mod;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
int mu(int x){
    if(x==1)return 1;
    int ans=1;
    for(int j=0;j<cnt&&prime[j]*prime[j]<=x;j++){
        if(x%prime[j]==0){
            int cnt=0;
            while(x%prime[j]==0){
                x/=prime[j];
                cnt++;
            }
            if(cnt!=1)return 0;
            ans=-ans;
        }
    }
    if(x!=1)ans=-ans;
    return ans;
}
int g(int x){
    return ppow(2,x-1,mod);
}
void init(){
    for(int i=2;i<maxm;i++){
        if(!notprime[i]){
            prime[cnt++]=i;
        }
        for(int j=0;j<cnt;j++){
        	if(prime[j]*i>=maxm)break;
            notprime[prime[j]*i]=1;//素数的倍数标记为非素数
            if(i%prime[j]==0)break;//看外面解释
        }
    }
}
signed main(){
    init();
    cin>>x>>y;
    if(y%x!=0){
        cout<<0<<endl;
        return 0;
    }
    int k=y/x;
    int ans=0;
    for(int i=1;i*i<=k;i++){
        if(k%i==0){
            ans+=mu(i)*g(k/i);
            if(k/i!=i){
                ans+=mu(k/i)*g(i);
            }
            ans%=mod;
        }
    }
    ans=(ans%mod+mod)%mod;
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值