ACMer 考虑算法时总会优先考虑时间复杂度,这里介绍几个优美的根号复杂度的算法(原来这个叫整除分块)。
可能需要 这里的基础知识
$s(n) = \sum_{i=1}^{n} \lfloor \frac{n}{i} \rfloor$
由于 $\lfloor \frac{n}{i} \rfloor$ 的取值个数不会超过 $2\sqrt{n}$,所以可能存在 $O(\sqrt{n})$ 的算法:
1 2 3 4 5 6 7 8
| LL getsum(LL n){ LL sum = 0; for(LL i=1,j;i<=n;i=j+1){ j = n/(n/i); sum += (j-i+1)*(n/i); } return sum; }
|
事实上, $s(n)$ 表示图像 $xy=1$ 下方正整点的个数
$\sigma_k(n) = \sum_{d|n} d^k$
- $\sigma_0(n)$ 表示正因子个数
- $\sigma_1(n)$ 表示正因子之和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| LL mypow(LL x,LL n){ LL r = 1; while(n){ if(n&1) r=r*x; n>>=1; x=x*x; } return r; } LL getr(LL n,LL k){ LL r = 0,d; for(d=1;d*d<n;++d){ if(n%d==0) r += mypow(d,k) + mypow(n/d,k); } if(d*d == n) r+=mypow(d,k); return r; }
|
$f_k(n) = \sum_{i=1}^n \sigma_k(i)$
如果我们已经得到 $ts[n] = \sum_{i=1}^n i^k$ 类似问题一,我们有以下 C++ 代码:
1 2 3 4 5 6 7 8
| LL getf(LL n){ LL sum = 0; for(LL i=1,j;i<=n;i=j+1){ j = n/(n/i); sum += (ts[j]-ts[i-1])*(n/i); } return sum; }
|
事实上我们有:
更一般的我们有
参考我的这篇博文
$g(n) = \sum_{i=1}^n\psi(i)$
这里 $\psi(n)$ 是欧拉函数表示小于 $n$ 且和 $n$ 互素的数的个数。
Euler’s 乘积公式:
我们现在开始计算 $g(n)$
我们定义
并且 $\sum_{i=1}^n g_k(i) = \sum_{1 \leq x \leq y \leq n} 1 = \frac{n(n+1)}{2}$ ,于是我们知道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const int N=1000006; LL ans[N]; void init(){ memset(ans,-1,sizeof(ans)); ans[1]=1;ans[2]=2; } LL getans(int n){ if(n<N&&ans[n]!=-1) return ans[n]; LL r = n*(n+1)/2; for(int i=2,j;i<=n;i=j+1){ j = n/(n/i); r -= (j-i+1)*getans(n/i); } if(n<N) ans[n]=r; return r; }
|
$f(n) = \sum_{i=1}^n \sum_{j=1}^n [\gcd(i,j)==1]$
实际上画图可知,$f(n)$ 还有一个表达式:
我们有不妨设 $n \leq m$
$r(n,m)=\sum_{i=1}^n \sum_{j=1}^m f(\gcd(i,j))$
特别地,$g(n, m) = \sum_{i=1}^n \sum_{j=1}^m [\gcd(i,j)==1] = \sum_{d=1}^n \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor \mu(d)$
Problem hdu5608
已知 $n^2 -3n+2 = \sum_{d|n} f(d)$ 计算 $h(n) = \sum_{i=1}^n f(i) \mod 10^9+7$
由于
我们知道
所以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include <cstring> #include <cstdio> #include <cstdlib> #include <ctime> #include <cmath> #include <iostream> #include <algorithm> #include <vector> #include <map> #include <set> #include <queue> #include <stack> #include <string> #include <bitset> using namespace std; typedef long long LL; typedef pair<int,int> PII; typedef pair<LL,LL> PLL; #define clr(a,b) memset(a,b,sizeof(a)) #define MP make_pair #define PB push_back #define lrt rt<<1 #define rrt rt<<1|1 #define lson l,m,lrt #define rson m+1,r,rrt const int N=1000006; const int M = 1e9+7; const int inv3 = (M+1)/3; int ans[N]; map<int,int> mp; void init(){ for(int i=0;i<N;++i){ ans[i] = LL(i-1)*(i-2)%M; } for(int i=1;i<N;++i){ for(int j=i<<1;j<N;j+=i){ ans[j] -= ans[i]; if(ans[j] < 0) ans[j] += M; } } for(int i=2;i<N;++i){ ans[i] += ans[i-1]; if(ans[i] > M) ans[i] -= M; } } int getans(int n){ if(n<N) return ans[n]; map<int,int>::iterator it = mp.find(n); if (it != mp.end()) return it->second; int r = LL(n)*(n-1)%M*(n-2)%M*inv3%M; for(int i=2,j;i<=n;i=j+1){ j = n/(n/i); r -= LL(j-i+1)*getans(n/i)%M; if(r<0) r+=M; } mp.insert(pair<int,int>(n,r)); return r; } int main(){ init(); int T,n; cin>>T; while(T--){ scanf("%d",&n); cout<<getans(n)<<endl; } return 0; }
|