[BZOJ3944]Sum-杜教筛
2017-06-19 23:59
323 查看
Sum
Description
Input
一共T+1行第1行为数据组数T(T<=10)
第2~T+1行每行一个非负整数N,代表一组询问
Output
一共T行,每行两个用空格分隔的数ans1,ans2Sample Input
61
2
8
13
30
2333
Sample Output
1 12 0
22 -2
58 -3
278 -3
1655470 2
很久以前,本蒟蒻很naive的认为O(n)的线性筛已经是最快的筛法了……
然而现在这个神奇的O(n23)的杜教筛完全推翻了本蒟蒻的想法…….
大千世界真是无奇不有…….
思路:
杜教筛入门题。
杜教筛可以用来解决一些积性函数的前缀和问题。
主要是利用了这类积性函数的一些神奇的性质来简化求值……
咱语文水平不高所以上面两句毫无意义的话可以无视。
不如我们来推一波?
看例子想必是最容易理解的了,尤其是同时看两个。
φ(n):
对于φ(n),有一个性质:∑d|nφ(d)=n
我们可以把它转化成这样:φ(n)=n−∑d|n,d<nφ(d)
定义它的前缀和为ϕ(n),有:
ϕ(n)=∑ni=1φ(i)
=∑ni=1i−∑d|i,d<iφ(d)
=n⋅(n+1)2−∑ni=2∑d|i,d<iφ(d)
=n⋅(n+1)2−∑ni=2∑⌊ni⌋d=1φ(d)
=n⋅(n+1)2−∑ni=2ϕ(⌊ni⌋)
然后因为⌊ni⌋的取值对于一段连续的i是相同的,咱就可以把相同的用乘法加速计算,而不是一个个去枚举了。
μ(n):
对于μ(n),同样有一个性质:[n=1]=∑d|nμ(d)
定义它的前缀和为M(n),有:
1=∑ni=1[i=1]
=∑ni=1∑d|iμ(d)
=∑ni=1∑⌊ni⌋d=1μ(d)
=∑ni=1M(⌊ni⌋)
所以M(n)=1−∑ni=2M(⌊ni⌋),同样可以用⌊ni⌋的相同取值来加速。
于是这题就可做了,用哈希或map来一波记忆化搜索,就可以做到上面的O(n23)了~
实现上,如果你和咱一样懒并且不怕自己的程序的时间复杂度多一个log,尽管和咱一样开map,时间还是可以接受的(反正只是一个不算太大的值)~
另外会爆int,直接开long long或者int计算时强转都是可以的~
#include<iostream> #include<map> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; typedef long long ll; typedef map<int,ll>::iterator m_it; const int N=5000000; int pri[N/5],tot; ll phi ,mu ; bool npri ; int n; map<int,ll>phis,mus; inline void init() { phi[1]=1; mu[1]=1; for(int i=2;i<N;i++) { if(!npri[i]) { pri[++tot]=i; mu[i]=-1; phi[i]=i-1; } for(int j=1;j<=tot && pri[j]*i<N;j++) { npri[pri[j]*i]=1; if(i%pri[j]) { phi[pri[j]*i]=(pri[j]-1)*phi[i]; mu[pri[j]*i]=-mu[i]; } else { phi[pri[j]*i]=phi[i]*pri[j]; mu[pri[j]*i]=0; break; } } } for(int i=1;i<N;i++) { phi[i]+=phi[i-1]; mu[i]+=mu[i-1]; } } ll calc_phi(ll n) { if(n<N) return phi ; m_it it; if((it=phis.find(n))!=phis.end()) return it->second; ll ret=n*(n+1)>>1,nxt; for(ll i=2;i<=n;i=nxt+1) { nxt=n/(n/i); ret-=(nxt-i+1)*calc_phi(n/i); } return phis =ret; } ll calc_mu(ll n) { if(n<N) return mu ; m_it it; if((it=mus.find(n))!=mus.end()) return it->second; ll ret=1,nxt; for(ll i=2;i<=n;i=nxt+1) { nxt=n/(n/i); ret-=(nxt-i+1)*calc_mu(n/i); } return mus =ret; } int main() { int T; scanf("%d",&T); init(); while(T--) { scanf("%d",&n); printf("%lld %lld\n",calc_phi(n),calc_mu(n)); } return 0; }
相关文章推荐
- bzoj 3944 Sum 杜教筛
- BZOJ 3944: Sum [杜教筛]
- bzoj 3944: Sum -- 杜教筛
- [bzoj3944]:Sum(杜教筛)
- [BZOJ 3944]Sum:杜教筛
- bzoj3944 Sum 杜教筛
- BZOJ.3944.Sum(杜教筛)
- [BZOJ3944]Sum(杜教筛)
- bzoj 3944: Sum 杜教筛
- [BZOJ3944]Sum(杜教筛)
- 杜教筛:Bzoj3944: sum
- BZOJ3944: Sum 杜教筛
- 【BZOJ3944】Sum(杜教筛)
- 杜教筛:Bzoj3944: sum
- [杜教筛] BZOJ3944: Sum
- 【BZOJ3944】Sum(杜教筛)
- BZOJ_3944_Sum_杜教筛
- 【BZOJ3944/4805】Sum/欧拉函数求和 杜教筛
- bzoj 3944: Sum(杜教筛)
- bzoj3944 Sum 杜教筛