bzoj3944: Sum(杜教筛)

题目

Description

S ( n ) = ∑ i = 1 n f ( i ) S(n)=\sum_{i=1}^n f(i) S(n)=i=1nf(i)

Solution

part1(核心公式推导):

对于任何函数 g ( i ) g(i) g(i),都有:
∑ i = 1 n ( f ∗ g ) ( i ) \sum_{i=1}^n(f*g)(i) i=1n(fg)(i)
= ∑ i = 1 n ∑ d ∣ i f ( d ) g ( i d ) =\sum_{i=1}^n\sum_{d|i}f(d)g(\frac{i}{d}) =i=1ndif(d)g(di)
= ∑ i = 1 n g ( i ) ∑ i ∗ j ≤ n f ( j ) =\sum_{i=1}^ng(i)\sum_{i*j≤n}f(j) =i=1ng(i)ijnf(j)
= ∑ i = 1 n g ( i ) S ( ⌊ n i ⌋ ) =\sum_{i=1}^ng(i)S(⌊\frac{n}{i}⌋) =i=1ng(i)S(in)
那么 g ( 1 ) S ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) − ∑ i = 2 n g ( i ) S ( ⌊ n i ⌋ ) g(1)S(n)=\sum_{i=1}^n(f*g)(i)-\sum_{i=2}^ng(i)S(⌊\frac{n}{i}⌋) g(1)S(n)=i=1n(fg)(i)i=2ng(i)S(in)
杜教筛就要构造一个 g ( i ) g(i) g(i),使得 ∑ i = 1 n ( f ∗ g ) ( i ) \sum_{i=1}^n(f*g)(i) i=1n(fg)(i) g ( i ) g(i) g(i)能快速计算

part2(复杂度证明):

假设计算 S ( n ) S(n) S(n)的复杂度为 T ( n ) T(n) T(n)
那么 T ( n ) = O ( n ) + ∑ i = 1 n T ( i ) + T ( n i ) T(n)=O(\sqrt n)+\sum_{i=1}^{\sqrt n}T(i)+T(\frac{n}{i}) T(n)=O(n )+i=1n T(i)+T(in) i i i 1 1 1枚举到 n n n时所有不同的 T ( ⌊ n i ⌋ ) T(⌊\frac{n}{i}⌋) T(in)之和)
t a n g j z tangjz tangjz大佬所说:这里只展开一层就可以了,更深层的复杂度是高阶小量
所以 T ( n ) = ∑ i = 1 n O ( i ) + O ( n i ) T(n)=\sum_{i=1}^{\sqrt n}O(\sqrt i)+O(\sqrt\frac{n}{i}) T(n)=i=1n O(i )+O(in )
= ∑ i = 1 n O ( n i ) =\sum_{i=1}^{\sqrt n}O(\sqrt\frac{n}{i}) =i=1n O(in )
= n ∫ 1 n x − 1 2 d x =\sqrt n\int_1^{\sqrt n}x^{-\frac{1}{2}}dx =n 1n x21dx
= n ⋅ 2 x 1 2 ∣ 1 n =\sqrt n\cdot 2x^{\frac{1}{2}}|_1^{\sqrt n} =n 2x211n
= O ( n 3 4 ) =O(n^\frac{3}{4}) =O(n43)
如果预处理了前 k ( k ≥ n ) k(k≥\sqrt n) k(kn )个数的前缀和,那么
T ( n ) = O ( k ) + ∑ i = 1 n k O ( n i ) = O ( k + n k ) T(n)=O(k)+\sum_{i=1}^\frac{n}{k}O(\sqrt\frac{n}{i})=O(k+\frac{n}{\sqrt k}) T(n)=O(k)+i=1knO(in )=O(k+k n)
k = O ( n 2 3 ) k=O(n^\frac{2}{3}) k=O(n32) T ( n ) = O ( n 2 3 ) T(n)=O(n^\frac{2}{3}) T(n)=O(n32)

part3(example):

就拿 φ ( i ) φ(i) φ(i)举例吧,有一个公式 φ ∗ 1 = i d 1 φ*1=id_1 φ1=id1
所以 1 ( 1 ) S ( n ) = ∑ i = 1 n i d 1 ( i ) − ∑ i = 2 n 1 ( i ) S ( ⌊ n i ⌋ ) 1(1)S(n)=\sum_{i=1}^nid_1(i)-\sum_{i=2}^n1(i)S(⌊\frac{n}{i}⌋) 1(1)S(n)=i=1nid1(i)i=2n1(i)S(in)
S ( n ) = n ( n + 1 ) 2 − ∑ i = 2 n S ( ⌊ n i ⌋ ) S(n)=\frac{n(n+1)}{2}-\sum_{i=2}^nS(⌊\frac{n}{i}⌋) S(n)=2n(n+1)i=2nS(in)

part4(代码实现中的注意点):

1. 1. 1.因为数据不止 1 1 1组,所以如果预处理 k k k个数的话,复杂度为 O ( k + T n k ) O(k+T\frac{n}{\sqrt k}) O(k+Tk n)
根据基本不等式, k k k ( T n ) 2 3 (Tn)^\frac{2}{3} (Tn)32最为合适
2. 2. 2.计算 n ( n + 1 ) 2 \frac{n(n+1)}{2} 2n(n+1)时,因为有 n + 1 n+1 n+1,所以可能会爆 i n t int int,数论分块中的 l l l r r r也是如此
3. 3. 3.关于记忆化,有两种方法。一种是开unordered_map,另外一种是假设现在正在计算 S ( n ′ ) S(n') S(n),那么只要判断 p [ ⌊ n n ′ ⌋ ] p[⌊\frac{n}{n'}⌋] p[nn]即可,数组大小可开到 O ( n k ) 的 规 模 O(\frac{n}{k})的规模 O(kn)

Code

#include<cstdio>
#include<cstring>
typedef unsigned long long ll;
typedef unsigned int ui;
const int N=5000000;
int T,i,pr[N/14],n,cnt,j,t,mu[N],m[1291],nn;
bool vis[N];
ll phi[N],p[1291];
ll calp(int n){
	if (n<N) return phi[n];
	if (p[nn/n]) return p[nn/n];
	ll ans=(1ll*n+1)*n>>1;
	for (ui l=2,r;l<=n;l=r+1) r=n/(n/l),ans-=calp(n/l)*(r-l+1);
	return p[nn/n]=ans;
}
int calm(int n){
	if (n<N) return mu[n];
	if (m[nn/n]) return m[nn/n];
	int ans=1;
	for (ui l=2,r;l<=n;l=r+1) r=n/(n/l),ans-=calm(n/l)*(r-l+1);
	return m[nn/n]=ans;
}
int main(){
	scanf("%d",&T);
	phi[1]=mu[1]=1;
	for (i=2;i<N;i++){
		if (!vis[i]) pr[cnt++]=i,phi[i]=i-1,mu[i]=-1;
		for (j=0;j<cnt && (t=i*pr[j])<N;j++){
			vis[t]=1;
			if (!(i%pr[j])){
				phi[t]=phi[i]*pr[j];
				mu[t]=0;
				break;
			}
			phi[t]=phi[i]*(pr[j]-1);
			mu[t]=-mu[i];
		}
	}
	for (i=2;i<N;i++) phi[i]+=phi[i-1],mu[i]+=mu[i-1];
	for (;T--;) scanf("%d",&n),nn=n,memset(p,0,sizeof(p)),
	memset(m,0,sizeof(m)),printf("%llu %d\n",calp(n),calm(n));
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值