[loj6053]简单的函数【min_25筛】

【题目链接】
  https://loj.ac/problem/6053
【题解】
  min_25筛的模板题。
  min_25筛可以解决以下一类积性函数的求和问题。
  1. f ( x ) ( x P ) 可以通过多项式表达出来。
  2. f ( x k ) ( x P ) 可以快速算出。
  这道题中, f ( x ) = x ^ 1 = x 1 ( x P x 2 ) 所以条件1满足,条件2显然也是满足的。
  part1
  我们先来考虑一个问题,如何求 F ( T ) = i = 1 T f ( i ) ( i P , T N / i ) ,T是N通过除法能得到的数,大约有 2 N 种取值。
  不难发现, i = 1 N f ( i ) ( i P ) = i = 1 N i 1 ( i P ) + 2 因为只有2异或了后会加1。
   那么我们要求的就是质数的和以及质数的个数。
   首先筛出 1.. N 中的所有质数,记作 p [ 1 ] . . p [ M ]
   记函数 G k ( n , m ) 表示 i = 1 n i k ( i P i > p [ m ] )
   那么相当于求出 G 0 ( T , M ) , G 1 ( T , M )
   考虑从 G k ( n , m 1 ) 推到 G k ( n , m ) ,就是从中去除最小质因子是 p [ m ] 的,于是可以推出:
   G k ( n , m ) = G k ( n , m 1 ) p [ m ] k ( G k ( n / p [ m ] , m 1 ) G k ( p [ m ] 1 , m 1 ) )
   相当于强制选出一个 p [ m ] ,剩下的随便选,但要重新加上有比 p [ m ] 更小的质因子的方案。
   边界条件是 G k ( n , 0 ) = i = 1 n i k ,可以 O ( 1 ) 计算。
   由于我们只要求 2 N 个取值,可以将它们一起考虑。
   具体来说,我们从前往后枚举每一个质数 p [ 1 ] . . p [ m ] 由于大的的取值不会影响小的,所以从后往前更新。
   现在的复杂度是 N ( s q r t N / l n N ) = O ( N / l n N )
   考虑优化,当 p [ m ] 2 > n 时, G k ( n , m ) = G k ( n , m 1 ) ,因为不可能存在一个有 p [ m ] 的因子但不是质数的数,所以每次有一段前缀不用算。
   现在的复杂度是 T i = 1 m [ p [ i ] 2 T ] O ( N 3 / 4 / l n N ) ?,我不会证,但肯定比 O ( N 3 / 4 ) 小。
   Part2
   处理出了 F 接下来就简单了。
   记 S ( n , m ) i = 1 n f ( i ) [ i p [ m ] ] ,我们要求的是 S ( N , 1 ) + f ( 1 )
   由于我们已经知道了 F (质数的答案和)。现在要加入非质数的答案。还是考虑枚举质数:
   S ( n , m ) = F ( n ) F ( p [ m ] 1 ) + i = m M j = 1 p [ i ] j + 1 N ( f ( p [ i ] j ) S ( n / p [ i ] j , m + 1 ) + f ( p [ i ] j + 1 ) )
   由于 p [ M ] N 所以它的 F 值也是已知的。
   边界条件是 n < p [ m ] 时,值为0。
   这个相当于是把上一部分的递归实现,所以复杂度与Part1相同。
   总时间复杂度: O ( N 3 / 4 / l n N ) 一般能做到 1 e 10
【代码】(为了便于调试很多地方写的比较烦)

    /* - - - - - - - - - - - - - - -
    User :      VanishD
    problem :   loj-6053
    Points :    Min_25 
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    P       1000000007
# define    N       1001000
using namespace std;
ll use[N], p[N], pre[N], bak[N], pre0[N], pre1[N], bak0[N], bak1[N];
ll sum, nt, cnt;
int pnum;
const ll inv2 = 5e8 + 4; 
void get_p(int n){
    use[1] = true; 
    for (int i = 2; i <= n; i++){
        if (!use[i]) p[++pnum] = i;
        for (ll j = 1; j <= pnum && 1ll * i * p[j] <= n; j++){
            use[i * p[j]] = true;
            if (i % p[j] == 0) break;
        }
    }
}
ll F(ll p, int k){
    if (k == 0) return 1;
    return p ^ k;
}
void get_Pow0(){
    for (int i = 1; i <= nt; i++)
        pre0[i] = i, bak0[i] = sum / i;
    for (int i = 1, prep = 1, bakp = nt; i <= pnum; i++){
        while (prep <= nt && p[i] * p[i] > prep) prep++;
        while (bakp >= 1  && p[i] * p[i] > sum / bakp) bakp--;
        for (int j = 1; j <= bakp; j++){
            ll x = sum / j;
            ll  tmp1 = (x <= nt) ? pre0[x] : bak0[sum / x],
                tmp2 = (x / p[i] <= nt) ? pre0[x / p[i]] : bak0[sum / (x / p[i])],
                tmp3 = pre0[p[i] - 1];
            bak0[j] = (tmp1 - 1 * (tmp2 - tmp3) + P) % P;
        }           
        for (int j = nt; j >= prep; j--){
            ll x = j;
            ll  tmp1 = (x <= nt) ? pre0[x] : bak0[sum / x],
                tmp2 = (x / p[i] <= nt) ? pre0[x / p[i]] : bak0[sum / (x / p[i])],
                tmp3 = pre0[p[i] - 1];
            pre0[j] = (tmp1 - 1 * (tmp2 - tmp3) + P) % P;
        }           
    }
}
void get_Pow1(){
    for (int i = 1; i <= nt; i++){
        pre1[i] = 1ll * i * (i + 1) % P * inv2 % P;
        ll tmp = (sum / i) % P;
        bak1[i] = tmp * (tmp + 1) % P * inv2 % P;
    }
    for (int i = 1, prep = 1, bakp = nt; i <= pnum; i++){
        while (prep <= nt && p[i] * p[i] > prep) prep++;
        while (bakp >= 1  && p[i] * p[i] > sum / bakp) bakp--;
        for (int j = 1; j <= bakp; j++){
            ll x = sum / j;
            ll  tmp1 = (x <= nt) ? pre1[x] : bak1[sum / x],
                tmp2 = (x / p[i] <= nt) ? pre1[x / p[i]] : bak1[sum / (x / p[i])],
                tmp3 = pre1[p[i] - 1];
            bak1[j] = (tmp1 - p[i] * (tmp2 - tmp3) % P + P) % P;
        }           
        for (int j = nt; j >= prep; j--){
            ll x = j;
            ll  tmp1 = (x <= nt) ? pre1[x] : bak1[sum / x],
                tmp2 = (x / p[i] <= nt) ? pre1[x / p[i]] : bak1[sum / (x / p[i])],
                tmp3 = pre1[p[i] - 1];
            pre1[j] = (tmp1 - p[i] * (tmp2 - tmp3) % P + P) % P;
        }
    }
}
ll get_s(ll n, int m){
    if (n <= 1 || n < p[m]) return 0;
    ll ans = ((n > nt) ? bak[sum / n] : pre[n]) - pre[p[m] - 1];
    for (int i = m; i <= pnum && n >= 1ll * p[i] * p[i]; i++)
        for (ll j = 1, k = p[i]; k * p[i] <= n; j++, k *= p[i])
            ans = (ans + F(p[i], j) * get_s(n / k, i + 1) + F(p[i], j + 1)) % P;
    return ans;
}
int main(){
    ll n;
    scanf("%lld", &n);
    sum = n; nt = sqrt(n);
    get_p(nt);
    get_Pow0(); get_Pow1(); 
    for (int i = 1; i <= nt; i++){
        if (i == 1) pre[i] = 0;
            else pre[i] = (pre1[i] - pre0[i] + 2 + P) % P;
        bak[i] = (bak1[i] - bak0[i] + 2 + P) % P;
    }
    p[pnum + 1] = nt + 1;
    printf("%lld\n", (get_s(n, 1) + 1) % P);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/d_vanisher/article/details/80457039