[LOJ572]Misaka Network 与求和

280 篇文章 1 订阅
12 篇文章 0 订阅

题目

传送门 to LOJ

思路

答案为
∑ i = 1 n f ( i ) k [ 2 ∑ j = 1 ⌊ n i ⌋ φ ( j ) − 1 ] \sum_{i=1}^{n}f(i)^k\left[2\sum_{j=1}^{\lfloor{n\over i}\rfloor}\varphi(j) -1\right] i=1nf(i)k 2j=1inφ(j)1

h ( x ) = f ( x ) k h(x)=f(x)^k h(x)=f(x)k 则问题在于求出 h h h h ∗ φ h\ast\varphi hφ 的前缀和。

由于 h h h 不是积性函数,所以传统的方法(如生成函数)都并不是很奏效。

用杜教筛求出 φ \varphi φ 的前缀和,则只需求出 h h h 的前缀和。

本题要我们从新的角度考察 m i n 25 \tt min25 min25 筛:将每个数质因数分解,只要能 把最大的质因数统一计算,复杂度就是正确的。

这当然可以做到,因为最大的质因数的贡献都是 1 1 1

m i n 25 \tt min25 min25 筛的 step two \text{step two} step two 中,分别返回 “已经确定次大质因数” 和 “单个大质数” 两种情况的结果,容易转移。

哦,对了,这玩意儿是不是该叫洲阁筛了?

代码

人尽皆知 { ⌊ n x ⌋ : x ∈ [ 1 , n ] } = { 1 , 2 , … , m , ⌊ n m ⌋ , ⌊ n m − 1 ⌋ , … , ⌊ n 1 ⌋ } \{\lfloor{n\over x}\rfloor:x\in[1,n]\}=\{1,2,\dots,m,\lfloor{n\over m}\rfloor,\lfloor{n\over m-1}\rfloor,\dots,\lfloor{n\over 1}\rfloor\} {⌊xn:x[1,n]}={1,2,,m,mn,m1n,,1n⌋}

#include <cstdio>
#include <algorithm> // Almighty XJX yyds!!
#include <cstring> // Who can tell me why I'm so weak!
#include <cctype> // rainybunny root of the evil.
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
    int a = 0, c = getchar(), f = 1;
    for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
    for(; isdigit(c); c=getchar()) a = a*10+(c^48);
    return a*f;
}

const int MAXN = 2000000000, PRELEN = 1587401;
int primes[PRELEN], primes_size;
unsigned phi[PRELEN+1];
bool is_prime[PRELEN+1];
void sieve(int n = PRELEN){
    memset(is_prime+2, true, n-1);
    phi[1] = 1; // as the definition
    for(int i=2,&len=primes_size; i<=n; ++i){
        if(is_prime[i]) primes[++len] = i, phi[i] = i-1;
        for(int j=1; j<=len; ++j){
            const llong to = llong(i)*primes[j];
            if(to > n) break;
            is_prime[to] = false;
            if(!(i%primes[j])){
                phi[to] = phi[i]*primes[j]; break;
            }
            else phi[to] = phi[i]*(primes[j]-1);
        }
    }
}

const int SQRTN = 44721;
double inv[SQRTN+1]; /** Barret Reduction */
unsigned coe[SQRTN]; /** pre-computed power of primes */
int w[SQRTN+1]; /** only for big ones */
unsigned cntp[2][SQRTN+1]; /** 0 for big, 1 for small */
unsigned sumf[2][SQRTN+1];
int min25(int n){
    int m = 1; /** floor( sqrt( @p n ) ) */
    while(llong(m+1)*(m+1) <= n) ++ m;
    rep(i,1,m) w[i] = int(n*inv[i]), cntp[0][i] = w[i]-1;
    rep(i,1,m) cntp[1][i] = i-1; // small
    for(int j=1; j<=primes_size; ++j){
        const llong bound = llong(primes[j])*primes[j];
        if(bound > n) break; // non-sense
        rep(i,1,m){ // from big to small
            if(w[i] < bound) break;
            int todiv = i*primes[j]; unsigned* from;
            if(todiv <= m) from = cntp[0]+todiv;
            else from = cntp[1]+int(w[i]*inv[primes[j]]);
            cntp[0][i] -= (*from)-unsigned(j-1);
        }
        drep(i,m,bound) cntp[1][i] -= cntp[1]
            [int(i*inv[primes[j]])]-unsigned(j-1);
    }
    drep(j,primes_size,1){
        const llong bound = llong(primes[j])*primes[j];
        if(bound > n) continue; // not essential
        rep(i,1,m){ // from big to small
            if(w[i] < bound) break;
            int todiv = i, tow = int(w[i]*inv[primes[j]]);
            for(todiv*=primes[j]; todiv<=m; todiv*=primes[j]){
                tow = int(tow*inv[primes[j]]); // keep moving
                sumf[0][i] += sumf[0][todiv]+(cntp[0][todiv]-j+1)*coe[j];
            }
            for(; tow>=primes[j]; tow=int(tow*inv[primes[j]]))
                sumf[0][i] += sumf[1][tow]+(cntp[1][tow]-j+1)*coe[j];
        }
        drep(i,m,bound) for(int tow=i; true; ){
            tow = int(tow*inv[primes[j]]);
            if(tow < primes[j]) break; // a bit too long
            sumf[1][i] += sumf[1][tow]+(cntp[1][tow]-j+1)*coe[j];
        }
    }
    return m;
}

unsigned sumphi[SQRTN];
unsigned qkpow(unsigned b, int q){
    unsigned a = 1;
    for(; q; q>>=1,b*=b) if(q&1) a *= b;
    return a;
}
int main(){
    int n = readint(), k = readint();
    sieve(); rep(i,1,PRELEN) phi[i] += phi[i-1];
    rep(i,1,SQRTN) inv[i] = double(1+1e-13)/i;
    const int bound = n/(PRELEN+1);
    for(int i=bound,m=1; i; --i){
        const int pos = int(n*inv[i]);
        while(llong(m+1)*(m+1) <= pos) ++ m;
        sumphi[i] = unsigned(llong(pos+1)*pos>>1)+m*phi[m];
        rep(j,1,m) sumphi[i] -= (phi[j]-phi[j-1])*unsigned(pos*inv[j]);
        int j = 2, todiv = i<<1;
        for(; todiv<=bound; ++j,todiv+=i) sumphi[i] -= sumphi[todiv];
        for(; j<=m; ++j) sumphi[i] -= phi[int(pos*inv[j])];
    }
    for(int i=1; primes[i]<=SQRTN; ++i)
        coe[i] = qkpow(primes[i],k);
    const int m = min25(n);
    rep(o,0,1) rep(i,1,m) sumf[o][i] += cntp[o][i];
    unsigned ans = -phi[m]*sumf[1][m];
    rep(i,1,bound) ans += sumphi[i]*(sumf[1][i]-sumf[1][i-1]);
    rep(i,bound+1,m) ans += (sumf[1][i]-sumf[1][i-1])*phi[int(n*inv[i])];
    rep(i,1,m) ans += (phi[i]-phi[i-1])*sumf[0][i];
    ans = (ans<<1)-sumf[0][1];
    printf("%u\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值