[LOJ572]Misaka Network 与求和

1464677-20190419110944874-273798928.png


题解

题解

题目让求\(\sum_{i=1}^{n}\sum_{j=1}^{n}f(gcd(i,j))^k\)
\(f\)代表次大质因子
先反演化简一波式子
\(\sum_{t=1}^{n}f(t)^k\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(i,j)]= t]\\ \sum_{t=1}^{n}f(t)^k\sum_{d=1}^{\frac{n}{t}}\mu(d)\lfloor\frac{n}{dt}\rfloor^2\\ \sum_{T=1}^{n}\lfloor\frac{n}{T}\rfloor^2\sum_{d|T}\mu(d)f(\frac{T}{d})^k\)
然后就可以去筛函数\(F(T)=\sum_{d|T}\mu(d)f(\frac{T}{d})^k\)
那么来考虑这玩意儿在非线性时间内怎么筛出来?
可以发现这个函数是用狄利克雷卷积来定义的,而且这个函数还包括了\(\mu\),所以可以考虑杜教筛
考虑用\(I\)\(F\)做狄利克雷卷积
\(I * \mu * f=(I * \mu)*f=e*f=f\)
所以我们在杜教筛中要快速求的狄利克雷卷积后的前缀和就是求\(f\)的前缀和了
\(f\)代表了次大质因子
所以可以考虑用\(min25\)筛来处理
具体做法就是先处理出\(g(i)\)表示\(\le i\)的质数的个数
然后考虑每个质数\(p_i\)的贡献
只有当分解到只剩下两个质数的时候再计算贡献
\(p_i\)的贡献就是当前所有比\(p_i\)大的质数的个数\(\times p_i^k\)
然后在枚举最小质因数的若干次方的时候顺便计算最大质因数和次大值因数相同的情况
具体就是这样筛的

int S(int x , int j) {
    if(x <= 1 || pri[j] > x) return 0 ; 
    int ret = prik[j - 1] * (g[Gid(x)] - j + 1) ;
    // 只剩下p[j-1]和另外一个比ta大的质数
    for(int i = j ; i <= pnum && pri[i] * pri[i] <= x ; i ++) {
        LL p1 = pri[i] , p2 = pri[i] * pri[i] ;
        for( ; p2 <= x ; p1 = p2 , p2 *= pri[i] ) 
            ret += S(x / p1 , i + 1) + prik[i] /*最大质因数和次大质因数相同的数 */;
    }
    return ret ;
}

总复杂度\(O(n^{\frac{3}{4}})\)
有点卡常数

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
# define LL long long
# define uit unsigned int 
const int M = 200005 ;
using namespace std ;

bool notp[M] , vis1[M] , vis2[M] ;
int n , m , sz , idk , pnum ;
int w[M] , id1[M] , id2[M] , pri[M] ;
uit ans , prik[M] , g[M] , f1[M] , f2[M] ;
inline int Gid(int x) {
    return (x <= sz) ? id1[x] : id2[n / x] ;
}
inline uit Fpw(uit Base , int k) {
    uit temp = 1 ;
    while(k) {
        if(k & 1) temp = temp * Base ;
        Base = Base * Base ; k >>= 1 ;
    }
    return temp ;
}
inline void presolve(int n) {
    for(int i = 2 ; i <= n ; i ++) {
        if(!notp[i]) pri[++pnum] = i , prik[pnum] = Fpw((uit)i , idk) ;
        for(int j = 1 ; j <= pnum && i * pri[j] <= n ; j ++) {
            notp[i * pri[j]] = true ;
            if(i % pri[j] == 0) break ;
        }
    }
}

uit S(int x , int j) {
    if(x <= 1 || pri[j] > x) return 0 ; 
    uit ret = prik[j - 1] * (g[Gid(x)] - j + 1) ;
    for(int i = j ; i <= pnum && pri[i] * pri[i] <= x ; i ++) {
        LL p1 = pri[i] , p2 = pri[i] * pri[i] ;
        for( ; p2 <= x ; p1 = p2 , p2 *= pri[i] ) 
            ret += S(x / p1 , i + 1) + prik[i] ;
    }
    return ret ;
}

inline uit Calc(int n) {
    if(vis2[Gid(n)]) return f2[Gid(n)] ;
    f2[Gid(n)] = S(n , 1) ; vis2[Gid(n)] = true ;
    return f2[Gid(n)] ;
}
uit Solve(int n) {
    if(n <= 1) return 0 ;
    if(vis1[Gid(n)]) return f1[Gid(n)] ;
    uit ret = Calc(n) + g[Gid(n)] ; 
    for(int l = 2 , r ; l <= n ; l = r + 1) {
        r = n / (n / l) ;
        ret -= (r - l + 1) * Solve(n / l) ;
    }
    vis1[Gid(n)] = true ; f1[Gid(n)] = ret ;
    return ret ;
}
int main() {
    scanf("%d%d",&n,&idk) ;
    sz = sqrt(n) ; presolve(sz) ;
    for(int l = 1 , r ; l <= n ; l = r + 1) {
        r = n / (n / l) ; w[++m] = n / l ;
        if(w[m] <= sz) id1[w[m]] = m ;
        else id2[n / w[m]] = m ;
        g[m] = w[m] - 1 ;
    }
    for(int j = 1 ; j <= pnum ; j ++)
        for(int i = 1 ; i <= m && pri[j] * pri[j] <= w[i] ; i ++)
            g[i] -= g[Gid(w[i] / pri[j])] - j + 1 ;
    for(int l = 1 , r ; l <= n ; l = r + 1) {
        r = n / (n / l) ;
        ans += (uit)(n / l) * (n / l) * (Solve(r) - Solve(l - 1)) ;
    }
    printf("%u\n",ans) ;
    return 0 ;
}

转载于:https://www.cnblogs.com/beretty/p/10734946.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值