51Nod1584 加权约数和

这题其实就是反演一波就好了(那你还推了一下午+一晚上),不过第一次碰到\(O(n\log n)\)预处理分块和式的方法……
不知为啥我跟唐教主的题解推的式子不太一样……(虽然本质上可能是相同的吧)
那就写一写好了,顺便骗点访问量(逃
\[\begin{align} \nonumber\text{Let}\space A=&\sum_{i=1}^n\sum_{j=1}^i i\sigma_1(ij),B=\sum_{i=1}^n i\sigma_1(i^2)\\ \nonumber\text{Then}\space Ans=&2A-B\\ \end{align}\]
\[\begin{align} \nonumber A=&\sum_{i=1}^n\sum_{j=1}^i i\sigma_1(ij)\\ \nonumber =&\sum_{i=1}^n\sum_{j=1}^i i\sum_{p|i}\sum_{q|j}[(p,q)=1]\frac{pj}q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{i=1}^n\sum_{j=1}^i i\sum _{p|i}\sum_{q|j}[d|(p,q)]\frac{pj}q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{d|p}p\left(\sum_{p|i}i\right)\sum_{d|q}\sum_{q|j}^i \frac j q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{p=1}^{\left\lfloor\frac n d\right\rfloor}dp\left(\sum_{p|i}^{\left\lfloor\frac n d\right\rfloor}di\right)\sum_{q=1}^{\left\lfloor\frac n d\right\rfloor}\sum_{q|j}^i \frac{dj}{dq}\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{p=1}^{\left\lfloor\frac n d\right\rfloor}dp\left(\sum_{p|i}^{\left\lfloor\frac n d\right\rfloor}di\right)\sum_{q=1}^i\sum_{q|j}^i \frac j q\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}i\left(\sum_{p|i}p\right)\sum_{j=1}^i\sum_{q|j}\frac j q\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}i\sigma_1(i)\sum_{j=1}^i\sigma_1(j)\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}i\sigma_1(i)S_{\sigma_1}(i) \end{align}\]
\[\begin{align} \nonumber B=&\sum_{i=1}^n i\sigma_1(i^2)\\ \nonumber =&\sum_{i=1}^n i\sum_{p|i}\sum_{q|j}[(p,q)=1]\frac{pi}q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{i=1}^n i\sum_{p|i}\sum_{q|j}[d|(p,q)]\frac{pi}q\\ \nonumber =&\sum_{d=1}^n\mu(d)\sum_{d|i} i\sum_{d|p|i}\sum_{d|q|j}\frac{pi}q\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor} i\sum_{p|i}\sum_{q|j}p\frac i q\\ \nonumber =&\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor} i\sigma_1^2(i)\\ \end{align}\]
\[Ans=2A-B\\=\sum_{d=1}^n\mu(d)d^2\sum_{i=1}^{\left\lfloor\frac n d\right\rfloor}i\sigma_1(i)\left(2S_{\sigma_1}(i)-\sigma_1(i)\right)\]
到了这里就可以用\(O(n)\)预处理+单次询问\(O(\sqrt n)\)的经典做法了,总复杂度\(O(n+T\sqrt n)\)
但是这样还是有点慢……常数优化到一定程度之后极限数据仍然要跑2s+(卡常技巧不行……逃),看来常数优化玩不了了,只能试试别的做法。
考虑枚举\(d\)\(k=\left\lfloor\frac n d\right\rfloor\),并考虑它们能对哪些\(n\)作出贡献。不难发现,如果\(\left\lfloor\frac n d\right\rfloor=k\),那么一定有\(n\in[dk,d(k+1))\),再稍加观察就能得到每对\((d,k)\)都会对\(\ge dk\)的所有\(n\)作出贡献,因此枚举所有\(d,k\)并差分一下,最后求一遍前缀和即可。
这样就能做到预处理\(O(n\log n)\),询问\(O(1)\)了,不用卡常也可以过。

#pragma GCC optimize("Ofast")
#include<stdio.h>
#define int unsigned
using namespace std;
template<class T>inline void readint(T &v){
    int x=0;
    char c=getchar();
    while(c<48)c=getchar();
    while(c>47){
        x=x*10+(c^48);
        c=getchar();
    }
    v=x;
}
template<class T>inline void writeint(T x){
    static int a[25];
    if(!x)putchar('0');
    else{
        int i=0;
        while(x){
            a[++i]=x%10;
            x/=10;
        }
        while(i)putchar(a[i--]^48);
    }
}
const int maxn=1000010,p=1000000007;
void get_table(int);
bool notp[maxn];
int prime[maxn],mu[maxn],sigma[maxn],ans[maxn],f[maxn];
signed main(){
    get_table(1000000);
    int T;
    readint(T);
    for(int k=1;k<=T;k++){
        int n;
        readint(n);
        putchar('C');
        putchar('a');
        putchar('s');
        putchar('e');
        putchar(' ');
        putchar('#');
        writeint(k);
        putchar(':');
        putchar(' ');
        writeint(ans[n]);
        putchar('\n');
    }
    return 0;
}
void get_table(int n){
    mu[1]=sigma[1]=1;
    for(int i=2;i<=n;i++){
        if(!notp[i]){
            prime[++prime[0]]=i;
            mu[i]=-1;
            sigma[i]=i+1;
            f[i]=1;
        }
        for(int j=1;j<=prime[0]&&i*prime[j]<=n;j++){
            notp[i*prime[j]]=true;
            if(i%prime[j]){
                mu[i*prime[j]]=-mu[i];
                sigma[i*prime[j]]=(long long)sigma[i]*(prime[j]+1)%p;
                f[i*prime[j]]=i;
            }
            else{
                if(f[i]==1)sigma[i*prime[j]]=((long long)prime[j]*sigma[i]+1)%p;
                else sigma[i*prime[j]]=(long long)sigma[i/f[i]*prime[j]]*sigma[f[i]]%p;
                f[i*prime[j]]=f[i];
            }
        }
    }
    for(int i=2,sum=1;i<=n;i++){
        sum+=sigma[i];
        if(sum>=p)sum-=p;
        sigma[i]=(long long)i*sigma[i]%p*(2ll*sum-sigma[i]+p)%p;
    }
    for(int i=1;i<=n;i++){
        int t=((long long)i*i%p*(signed)mu[i]+p)%p;
        for(int j=1;i*j<=n;j++)ans[i*j]=(ans[i*j]+(long long)t*sigma[j])%p;
    }
    for(int i=2;i<=n;i++){
        ans[i]+=ans[i-1];
        if(ans[i]>=p)ans[i]%=p;
    }
}

转载于:https://www.cnblogs.com/hzoier/p/7105009.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值