码迷,mamicode.com
首页 > 其他好文 > 详细

P4213 【模板】杜教筛(Sum)

时间:2018-12-23 23:55:30      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:==   约数个数   范围   const   begin   int   its   redo   else   

\(\color{#0066ff}{题 目 描 述}\)

给定一个正整数\(N(N\le2^{31}-1)\)

\(\begin{aligned} ans_1=\sum_{i=1}^n\varphi(i) \end{aligned}\)

\(\begin{aligned} ans_2=\sum_{i=1}^n \mu(i) \end{aligned}\)

\(\color{#0066ff}{输 入 格 式}\)

一共T+1行
第1行为数据组数T(T<=10)
第2~T+1行每行一个非负整数N,代表一组询问

\(\color{ #0066ff }{ 输 出 格 式 }\)

一共T行,每行两个用空格分隔的数ans1,ans2

\(\color{#0066ff}{输入样例}\)

6
1
2
8
13
30
2333    

\(\color{#0066ff}{ 输 出 样 例}\)

1 1
2 0
22 -2
58 -3
278 -3
1655470 2

\(\color{#0066ff}{数 据 范 围 与 提 示}\)

\(N \leq 2^{31}\)

\(\color{#0066ff}{题 解}\)

前置知识1 : 狄利克雷卷积

对于任意函数f,g,有\(\begin{aligned} h(i) = \sum_{d|i}f(d)*g(\frac{n}{d})\end{aligned}\)

h即为f和g的卷积

常用函数

1、\(i(n) = 1\)

2、\(id(n) = n\)

3、\(e(n)=\left\{\begin{aligned}1\ \ \ n = 1 \\ 0 \ \ \ n \neq 1\end{aligned}\right.\)

4、欧拉函数\(\varphi(n)\)

5、懵逼钨丝函数\(\mu(n)=\left\{\begin{aligned}1\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ n = 1 \\ (-1)^k \ \ \ n由k个不同质数相乘得到\\ 0\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 其它情况\end{aligned}\right.\)

6、\(\sigma(n)=n的约数和\)

7、\(d(n)=n的约数个数\)

常用卷积

1、\(i*\mu = e\)

2、\(e*a=a\)

3、\(\mu * id= \varphi\)

4、\(i*id=\sigma\)

5、\(i*i=d\)

6、\(i*\varphi=id\)

杜教筛

已知\(f(i)\)

用来求\(\begin{aligned}\sum_{i = 1}^n f(i)\end{aligned},n\leq 2^{31}\)

定义\(h(i)=(f*g)(i)=\begin{aligned}\sum_{d|i}f(d)*g(\frac{i}{d})\end{aligned}\)

\(\displaystyle\sum_{i=1}^nh(i)\)

用定义展开

\(=\displaystyle\sum_{i=1}^n\sum_{d|i}g(d)f\left(\frac i d\right)\)

d的范围也是【1.n】的,所以改成枚举d,找它的倍数,这个式子是在求和,找全了就行

\(=\displaystyle \sum_{d=1}^ng(d)\sum_{d|i}f\left(\frac i d \right)\)

把后面变一下

\(=\displaystyle \sum_{d=1}^ng(d)\sum_{i=1}^{\left\lfloor\frac n d \right \rfloor}f( i)\)

然后

\(=\displaystyle \sum_{i=1}^ng(i)S\left(\left\lfloor\frac n i\right\rfloor\right)\)

所以

\(\displaystyle \sum_{i=1}^nh(i)=\sum_{i=1}^ng(i)S\left(\left\lfloor\frac n i\right\rfloor\right)\)

有一个好像没用的式子

\(\displaystyle g(1)S(n)=\sum_{i=1}^ng(i)S\left(\left\lfloor\frac n i\right\rfloor\right)-\sum_{i=2}^ng(i)S\left(\left\lfloor\frac n i\right\rfloor\right)\)

上式把后面移项就成恒等式了

我们把右面第一项用刚刚的结论换走

\(\displaystyle g(1)S(n)=\sum_{i=1}^nh(i)-\sum_{i=2}^ng(i)S\left(\left\lfloor\frac n i\right\rfloor\right)\)

这。。是个递归式

就没了

对于S的递归,用数列分块

一般的h和g都很好求(构造)

对于本题来说

\(i*\varphi=id\)

所以对于\(\varphi\)

\(\displaystyle S(n)=\frac{n*(n+1)}{2}-\sum_{i=2}^nS\left(\left\lfloor\frac n i\right\rfloor\right)\)

刚刚有\(i*\mu=e\)

所以

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

没了。。。

把前\(4*10^6\)的东西线性筛一下

最后的复杂度\(O(n^{\frac{2}{3}})\)不会证

#include <bits/stdc++.h>

typedef long long LL;

const int maxn = 4e6;
const int maxx = 4e6 + 10;

int in() {
    char ch; int x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    return x * f;
}

bool vis[maxx];
LL phi[maxx];
int mu[maxx], pri[maxx], tot; 
std::map<int, LL> P; 
std::map<int, int> M;

void predoit() {
    phi[1] = mu[1] = 1LL;
    for(int i = 2; i <= maxn; i++) {
        if(!vis[i]) {
            pri[++tot] = i;
            phi[i] = i - 1;
            mu[i] = -1;
        }
        for(int j = 1; j <= tot && i * pri[j] <= maxn; j++) {
            vis[i * pri[j]] = true;
            if(i % pri[j] == 0) {
                phi[i * pri[j]] = phi[i] * pri[j];
                mu[i * pri[j]] = 0;
                break;
            }
            else {
                phi[i * pri[j]] = phi[i] * (pri[j] - 1);
                mu[i * pri[j]] = -mu[i];
            }
        }
    }
    for(int i = 2; i <= maxn; i++) {
        phi[i] += phi[i - 1];
        mu[i] += mu[i - 1];
    }
}

LL workphi(int now)
{
    if(now <= maxn) return phi[now];
    if(P.count(now)) return P[now];
    LL ans = now * (now + 1LL) / 2;
    for(int i = 2, lst; i <= now; i = lst + 1) {
        lst = now / (now / i);
        ans -= 1LL * (lst - i + 1LL) * workphi(now / i);
    }
    return P[now] = ans;
}

int workmu(int now)
{
    if(now <= maxn) return mu[now];
    if(M.count(now)) return M[now];
    int ans = 1;
    for(int i = 2, lst; i <= now; i = lst + 1) {
        lst = now / (now / i);
        ans -= workmu(now / i) * (lst - i + 1);
    }
    return M[now] = ans;
}

int main() {
    predoit();
    for(int T = in(); T --> 0;) {
        int n = in();
        printf("%lld %d\n", workphi(n), workmu(n));
    }
    return 0;
}

P4213 【模板】杜教筛(Sum)

标签:==   约数个数   范围   const   begin   int   its   redo   else   

原文地址:https://www.cnblogs.com/olinr/p/10165706.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!