D、brz的函数(牛客练习赛72)

D、brz的函数

\( \sum_{i=1}^n\sum_{j=1}^n\mu(ij)=\sum_{i=1}^n\sum_{j=1}^n\mu(i)\mu(j)[gcd(i,j)=1]=\sum_{i=1}^n\sum_{j=1}^n\mu(i)\mu(j)\sum_{d|gcd(i,j)}\mu(d)=\sum_{i=1}^n\sum_{j=1}^n\mu(i)\mu(j)\sum_{d|i,j}\mu(d)=\sum_{d=1}^n\mu(d)\sum_{d|i}\mu(i)\sum_{d|j}\mu(j)=\sum_{d=1}^n\mu(d)\left(\sum_{d|i}\mu(i)\right)^2 \)
将公式化简如上,发现该式子只需要求出两部分\(\mu\)函数以及\(\sum_{d|i}\mu(i)\)(某个因子\(d\)倍数的\(\mu\)函数和),发现无法在T循环内求出,只能预先处理,定义两个新变量数组mu_fac_sum和ans.mu_fac_sum[i]表示因子i倍数的\(\mu\)之和;ans[i]表示n==i时原式答案,我们预先处理每一个n,发现n-1的值会对n的值有所贡献,以及n的因子中增值的贡献,时间复杂度\(O(nlogn+n^{\frac{3}{2}})\)

#include <bits/stdc++.h>

using namespace std;

const int NMAX = 5e4 + 2;
typedef long long ll;
ll mu[NMAX],mu_fac_sum[NMAX],ans[NMAX];
int prime[NMAX],isw[NMAX],tot;

void init()
{
    mu[1] = 1;
    for(int i = 2; i < NMAX; i++)
    {
        if( !isw[i] )
        {
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot; j++)
        {
            if(i * prime[j] >= NMAX) break;
            isw[i * prime[j]] = true;
            if( i % prime[j] == 0)
            {
                mu[i * prime[j]] = 0;
                break;
            }
            else
            {
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
    ans[1] = 1;
    mu_fac_sum[1] = 1;
    for(int i = 2;i < NMAX;i++)
    {
        ans[i] = ans[i - 1];
        for(int j = 1;j * j <= i;j++)
        {
            if(i % j == 0)
            {
                ans[i] += mu[j]*(mu[i]*mu[i]+2*mu[i]*mu_fac_sum[j]);
                mu_fac_sum[j] += mu[i];
                if(j * j != i)
                {
                    ans[i] += mu[i/j]*(mu[i]*mu[i]+2*mu[i]*mu_fac_sum[i/j]);
                    mu_fac_sum[i/j] += mu[i];
                }
            }
        }
    }
}

int main()
{
    int t, n;
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        printf("%lld\n",ans[n]);
    }
    return 0;
}
posted @ 2020-11-06 22:22  jadelemon  阅读(171)  评论(0编辑  收藏  举报