2018 ICPC徐州网络赛 D. Easy Math

题目链接

先放一个问答区的图

\sum_{i=1}^{m}\mu \left ( i*n \right )

考虑莫比乌斯函数的定义,当n = k*p*p (p > 1)时,\mu \left ( n \right )=0

又考虑到莫比乌斯函数作为积性函数的性质,则有:\sum_{i=1}^{m}\mu \left ( i*n \right )=\mu \left ( n \right )\sum_{i=1}^{m}\left [ gcd\left (i,n \right ) ==1\right ]\mu \left ( i \right )

由容斥原理:\sum_{i=1}^{m}\left [ gcd\left (i,n \right ) ==1\right ]\mu \left ( i \right )=\sum_{d|n}^{ }\mu \left ( d \right )\sum_{k=1}^{\left [ \frac{m}{d} \right ]}\mu \left ( k*d \right )

令: f(m, n)=\sum_{i=1}^{m}\mu \left ( i*n \right ),则:f\left ( m,n \right )=\mu \left ( n \right )\sum_{d|n}^{ }\mu \left ( d \right )f\left ( \left [ \frac{m}{d} \right ],d \right )

先预处理n的质因子,就可以加速\mu \left ( d \right )的计算,递归地进行处理。

考虑处理到边界时,n = 1即为求\sum_{i=1}^{m}\mu \left ( i \right )。预处理\sqrt{n}以内的数,其余部分用杜教筛来进行处理。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll maxn = 1000050;
const ll INF = (1LL << 62) - 1;
const ll mod = 1000000007;

int no, num;
bool ispri[maxn];
ll pri[maxn], p[maxn], prif[25];
ll mu[maxn], summu[maxn];
struct node
{
    ll x, y;
    node(ll _x, ll _y):x(_x), y(_y){}
    bool operator < (node a)const
    {
        if(x == a.x)return y < a.y;
        return x < a.x;
    }
};
map<node, ll>mp;
map<ll, ll>dmap;

void init()
{
    mp.clear(), dmap.clear();
    no = 0;
    memset(ispri, -1, sizeof(ispri));
    memset(mu, 0, sizeof(mu));
    mu[1] = 1;
    for(ll i = 2;i < maxn;i++)
    {
        if(ispri[i] != 0) pri[no++] = i, mu[i] = -1;
        for(int j = 0;j < no && i*pri[j] < maxn;j++)
        {
            ispri[i*pri[j]] = false;
            if(i % pri[j] == 0) break;
            mu[i*pri[j]] = -mu[i];
        }
    }
    summu[1] = 1;
    for(int i = 2; i < maxn;i++)
        summu[i] = summu[i-1] + mu[i];
}

bool getpri(ll n)
{
    num = 0;
    for(int i = 0;i < no && pri[i]*pri[i] <= n;i++)
    {
        if(n % pri[i] == 0)
        {
            if((n/pri[i]) % pri[i] == 0) return false;
            p[num++] = pri[i];
            while(n % pri[i] == 0) n /= pri[i];
        }
    }
    if(n != 1) p[num++] = n;
    return true;
}

ll sol(ll n)
{
    if(n < maxn) return summu[n];
    if(dmap.count(n)) return dmap[n];
    ll ans = 1, p = 0;
    for(ll i = 2;i <= n;i = p + 1)
    {
        p = n/(n/i);
        ans -= sol(n/i)*(p-i+1);
    }
    return dmap[n] = ans;
}

ll cal(ll m, ll n)
{
    node cnm(m, n);
    if(mp.count(cnm)) return mp[cnm];
    if(n == 1) return sol(m);
    ll prif[25];
    ll ans = 0, numf = 0;
    for(int i = 0;i < num;i++)
    {
        if(n % p[i] == 0)
            prif[numf++] = p[i];
    }
    for(int i = 0;i < (1<<numf);i++)
    {
        ll d = 1, mud = 1;
        for(int j = 0;j < numf;j++)
        {
            if((i>>j) & 1)
            {
                d *= prif[j];
                mud = -mud;
            }
        }
        if(m >= d) ans += mud*cal(m/d, d);
    }
    return mp[cnm] = numf%2 ? -ans : ans;
}

int main()
{
    init();
    ll m, n;
    scanf("%lld%lld", &m, &n);
    if(getpri(n)) printf("%lld\n", cal(m, n));
   	else printf("0\n");
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值