2018 ICPC 徐州 計蒜客 - Easy Math

2018 ICPC 徐州 計蒜客 - Easy Math

題目給定m<2109,n<1012 計算:

ans=i=1mμ(in)

顯然: μ(n)=0ans=0
mu(n)!=0 時:
ans=i=1mμ(in)=μ(n)i=1mμ(i)[gcd(i,n)=1]

記:
f(k)=i=1mμ(i)[gcd(i,n)==k]F(k)=k|df(d)

顯然,這裏F(k) 代表下指標遍歷所有gcdk 的倍數的情況,那麼k 必須是n 的約數纔有意義:
F(k)=[k|n]i=1mkμ(ik)

反演有:
f(k)=k|dμ(dk)F(d)

則:
ans=μ(n)f(1)=μ(n)d=1mμ(d)[d|n]i=1mdμ(id)=μ(n)d|nμ(d)i=1mdμ(id)

記:
A(m,n)=ans=i=1mμ(in)

顯然:

A(m,n)=μ(n)d|nμ(d)A(md,d)A(0,k)=0A(k,1)=M(k)A(1,k)=μ(k)

其中,M(n)=i=1nμ(i) 計算方法參見:https://blog.csdn.net/ZLH_HHHH/article/details/77860249
在計算M 的同事保留計算A所需的計算信息
計算A的時間可以表示爲:

T(m,n)=d|nT(md,d)<O(mlog n)

總時間複雜度不超過:
O(mlog n)+O(m0.75)
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <cmath>
#define MAXN 2000005
using namespace std;
typedef long long LL;
LL M[MAXN] = { 0,-1 };
LL tmp[MAXN];
LL mu[MAXN + 2];
void clat(LL n, int d)
{
    LL ans = 0;
    int m = (int)sqrt(n) + 1;
    for (int L = 1;L<m;L++)
        ans += M[L] * (n / L - n / (L + 1));
    for (int i = (int)(n / m);i>1;i--)
    {
        LL u = n / i;
        if (u<MAXN)
            ans += M[u];
        else
            ans += tmp[i*d];
    }
    tmp[d] = 1 - ans;
}

LL slove(LL n)
{
    if (n<MAXN)return M[n];
    for (int i = (int)(n / (MAXN - 1));i;i--) clat(n / (LL)i, i);
    return tmp[1];
}

LL DFS(LL m, LL c, LL n, LL mn)
{
    if (n == 1)
    {
        if (m < MAXN)
            return M[m];
        else
            return tmp[c];
    }
    if (m == 1)return mn;
    if (m == 0)return 0;
    LL ans = 0;
    for (LL d = 1;d*d < n;d++)
    {
        if (n%d)continue;
        ans += mu[d] * DFS(m / d, c*d, d, mu[d]);
        int u_ = mn / mu[d];
        LL d_ = n / (LL)d;
        ans += u_*DFS(m / d_, c*d_, d_, u_);
    }
    return ans*mn;
}

int main()
{
    for (int i = 1;i<MAXN;i++)
    {
        M[i] = -M[i];
        mu[i] = M[i];
        for (int j = i << 1;j<MAXN;j += i)M[j] += M[i];
        M[i] += M[i - 1];
    }
    LL n, m;
    scanf("%lld %lld", &m, &n);
    LL mn = 1;
    slove(m);
    if (n >= MAXN)
    {
        LL a = n;
        for (LL i = 2;i*i <= n;i++)
        {
            if (a%i == 0)
            {
                a /= i;
                mn = -mn;
                if (a%i == 0)
                {
                    mn = 0;
                    break;
                }
            }
        }
        if (mn != 0 && a > 1)mn = -mn;
    }
    else
        mn = mu[n];
    if (mn == 0)
    {
        printf("0\n");
        return 0;
    }
    printf("%lld\n", DFS(m, 1, n, mn));
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章