BZOJ2401: 陶陶的难题I

42 篇文章 0 订阅

这道题和 ni=1mj=1LCM(i,j) 很像,但是这道题n和m是相同的,而且需要 O(1) 询问
所以尝试设 f(n)=ni=1nj=1LCM(i,j) ,但 f 不是积性函数,所以不考虑i,重新定义f(n)=ni=1LCM(i,n),这样 f 的前缀和在LCM的矩阵上就是一个三角形,整个矩阵的LCM和也能求
这里写图片描述
(这是个n*n的LCM的矩阵,横坐标是i,纵坐标是j,每个位置存了LCM(i,j),那么答案就是整个矩阵的和,而f的前缀和是矩阵里过对角线的一个三角形)
f(n)=ni=1ingcd(i,n)>f(n)=nd|nndi=1i[gcd(i,n/d)=1]
g(n)=ni=1i[gcd(i,n)=1] ,则 g(n)=nφ(n)2+12[n=1] f(n)=n(d|ng(nd)+12) ,但是这样两个函数都不是积性函数,也没法预处理 ,所以令 g(n)21 ,得 g(n)=nφ(n) ,这样 g(n) 就是积性函数, f(n) 也是了(不考虑前面的系数n),然后就可以线性筛 f,g f 的前缀和,因为原来的f的前缀和是三角形,算答案需要*2减对角线,而我们对于 g 的变换刚好和算答案时对f的变换相同,所以答案就是 f <script type="math/tex" id="MathJax-Element-100">f</script>的前缀和,直接输出即可



出题人你取个模能死?非要我打高精…….



code: (程序里的f是上文的g,g是上文的f)

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn = 1000001;
const long long M = 1e9;
struct ll
{
    long long a,b;
    ll(){}
    ll(long long _a,long long _b)
    {
        a=_a;b=_b;
        a+=b/M;
        b%=M;
    }
};
ll operator +(ll a,ll b)
{
    ll ret=a;
    ret.a+=b.a; ret.b+=b.b;
    ret.a+=ret.b/M; ret.b%=M;
    return ret;
}
ll operator -(ll a,ll b)
{
    ll ret=a;
    ret.a-=b.a;
    if(ret.b<b.b)ret.a--,ret.b+=M;
    ret.b-=b.b;
    return ret;
}
ll operator *(ll a,ll b)
{
    ll ret;
    ret.a=a.a*b.a+a.b*b.a+a.a*b.b;
    ret.a+=a.b*b.b/M;
    ret.b=a.b*b.b%M;
    return ret;
}

void putout(ll x)
{
    if(x.a) 
    {
        printf("%lld",x.a);
        printf("%09lld\n",x.b);
    }
    else printf("%lld\n",x.b);
}

long long p[maxn],pri,phi[maxn];
ll f[maxn],g[maxn],sum[maxn];
bool v[maxn];
long long n,m;

int main()
{
    phi[1]=1;
    f[1]=g[1]=ll(0,1); sum[1]=ll(0,1);
    for(long long i=2;i<maxn;i++)
    {
        if(!v[i])
        {
            p[++pri]=i;
            phi[i]=i-1;
            f[i]=ll(0,i)*ll(0,phi[i]);
            g[i]=f[i]+f[1];
        }
        for(long long j=1;j<=pri&&i*p[j]<maxn;j++)
        {
            long long k=p[j]*i;
            v[k]=true;
            if(i%p[j]==0)
            {
                phi[k]=phi[i]*p[j];
                long long temp=k,tmp=1;
                ll tt=ll(0,0);
                while(temp%p[j]==0) temp/=p[j],tmp*=p[j],tt=tt+f[temp];
                if(temp==1)
                {
                    f[k]=ll(0,k)*ll(0,phi[k]);
                    g[k]=f[k]+tt;
                }
                else
                {
                    f[k]=f[temp]*f[tmp];
                    g[k]=g[temp]*g[tmp];
                }
                break;
            }
            else
            {
                phi[k]=phi[i]*phi[p[j]];
                f[k]=f[i]*f[p[j]];
                g[k]=g[i]*g[p[j]];
            }
        }
        sum[i]=sum[i-1]+g[i]*ll(0,i);
    }

    int t; scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&n);
        ll ans=sum[n];
        putout(ans);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值