2019年ACM-ICPC - 南京网络赛E:K Sum【反演+杜教筛】

题目:

2019年ICPC南京网络赛E:K Sum

题意:

给定\small N\small K;求出下面又长又难看的式子的值:

\small \sum_{i=2}^{k}f_n(i)=\sum_{i=2}^{k}\sum_{l_1}^{n}\sum_{l_2}^{n}...\sum_{l_i}^{n}(gcd(l_1,l_2,...,l_i))^2

分析:

首先从简单的推一下(纯属自己娱乐):

\small \sum_{i=1}^{N}\sum_{j=1}^{N}(gcd(i,j))^2

\small =\sum_{t=1}^{N}\sum_{i=1}^{\left \lfloor \frac{N}{t} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{N}{t} \right \rfloor}\sum_{d|gcd(i,j)}t^2\mu (d)

\small =\sum_{t=1}^{N}t^2\sum_{d=1}^{\left \lfloor \frac{N}{t} \right \rfloor}\mu(d)\left \lfloor \frac{N}{td} \right \rfloor^2

\small =\sum_{l=1}^{N}\left \lfloor \frac{N}{l} \right \rfloor^2\sum_{d|l}\mu(d)\frac{l^2}{d^2}

那么原式就可以化简成如下形式:

\small =\sum_{i=2}^{k}\sum_{l=1}^{n}\left \lfloor \frac{n}{l} \right \rfloor^i\sum_{d|l}\mu(d)\frac{l^2}{d^2}

\small =\sum_{l=1}^{n}(\sum_{i=2}^{k}\left \lfloor \frac{n}{l} \right \rfloor^i)(\sum_{d|l}\mu(d)\frac{l^2}{d^2})

\small =\sum_{l=1}^{n}(\sum_{i=2}^{k}\left \lfloor \frac{n}{l} \right \rfloor^i)g(l)

把最后面的一堆设成函数g(L),再看这个式子中间的部分,就是一个等比数列,而向下取整的值是一块一块的,每一块是相等的,可以利用这个性质加速,那么我们还得快速计算出g(L1) - g(L2),设S(n)为g(L)的前n项和,那么就等价于S(L1) - S(L2);这个函数的前缀和就得用杜教筛快速求出;容易看出g函数是莫比乌斯函数\mu和单位函数id^2的狄利克雷卷积的形式,而莫比乌斯函数卷上恒等函数等于元函数,设h(n) = (g*I)(n),则h(n) = id(n)^2 = n^2,(这部分有点水,熟悉了再来填坑)即得到:

S(n) = \sum_{i=1}^{N}i^2-\sum_{d=2}^{N}S(\left \lfloor \frac{N}{d} \right \rfloor)

利用上式即可完成杜教筛部分,等比数列求和可用欧拉降幂公式简化,注意公比为1的情况(这时求和答案为k-1,应对mod取余)

代码:

#include <bits/stdc++.h>

#define sz(x) (int)(x).size()
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
const int maxn = 1e6+16;
const int inv6 = 166666668;
map<int,int> mp;
string s;
int ans[maxn],mob[maxn],prime[maxn],vis[maxn];
void init(){                     //O(nlogn)预处理g函数的部分前缀和
    mob[1] = 1;int cnt = 0;
    for(int i = 2;i < maxn; ++i){
        if(!vis[i]) prime[cnt++]=i,mob[i]=-1;
        for(int j = 0;j < cnt&&1ll*i*prime[j]<maxn; ++j){
            vis[i*prime[j]] = 1;
            if(i%prime[j] == 0){
                mob[i*prime[j]] = 0;
                break;
            }
            else mob[i*prime[j]] = -1*mob[i];
        }
    }
    for(int i = 1;i < maxn; ++i){
        for(int j = i;j < maxn; j+=i){
            ans[j] += ((1ll*j/i)*(1ll*j/i)%mod*mob[i]%mod+mod)%mod;
            if(ans[j] >= mod) ans[j] -= mod;
        }
    }
    for(int i = 1;i < maxn; ++i){
        ans[i] += ans[i-1];
        if(ans[i] >= mod) ans[i] -= mod;
    }
}
LL qpow(LL a,LL x,LL mod){
    LL res = 1; a%=mod;
    while(x){
        if(x & 1) res = res*a % mod;
        a = a*a % mod;
        x >>= 1;
    }
    return res;
}
LL inv(LL a,LL mod){
    return qpow(a,mod-2,mod);
}
LL cal1(int n){               //杜教筛
    if(n < maxn) return ans[n];
    if(mp.count(n)) return mp[n];
    LL res = 0;
    for(int i = 2,last;i <= n; i=last+1){
        int tep = n/i; last = n/tep;
        res += 1ll*(last-i+1)*cal1(tep)%mod;
        if(res>=mod) res -= mod; 
    }
    return mp[n] = (1ll*n*(n+1)%mod*(2*n+1)%mod*inv6%mod-res+mod)%mod;
}
LL cal2(LL q,LL n,LL nn){     //等比数列求和
    if(q == 1) return (nn-1+mod)%mod;
    return inv(q-1,mod)*(qpow(q,n+1,mod)-q*q%mod+mod)%mod; 
}
LL solve(LL n){
    LL res = 0,k = 0,kk = 0;
    for(int i = 0;i < sz(s); ++i){
        k = (k*10+(s[i]-'0'))%(mod-1);
        kk = (kk*10+(s[i]-'0'))%mod;
    }
    for(int i = 1,last;i <= n; i=last+1){
        int tep = n/i; last = n/tep;
        res += cal2(tep,k,kk)*(cal1(last)-cal1(i-1)+mod)%mod;
        if(res >= mod) res -= mod;
    }
    return res%mod;
}
int main(){
    int T,n; 
    init(); cin >> T;
    while(T--){
        cin >> n >> s;
        cout << solve(n) << '\n';
    }
    return 0;
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值