题解 P2424 【约数和】

shenbear

2019-09-27 18:44:20

Solution

# 除法分块模板题 首先,先不说除法分块,我们先想思路 ### 1.暴力枚举 ```cpp #include <bits/stdc++.h> using namespace std; int a,b; int f(int x) { int s=0; for(int i=1;i*i<=x;i++) { if(x%i==0) s+=i+x/i; if(i*i==x) s-=i; } // printf("%d %d\n",x,s); return s; } int main() { int sum=0; cin>>a>>b; for(int i=a;i<=b;i++) { sum+=f(i); } cout<<sum; return 0; } ``` 直接枚举所有数,再求约数之和 复杂度O(n^3/2) 20分 ### 2.设f[x]为1~x的约数和,我们发现所求的,就是f[b]-f]a] 对于求1~x的约数之和,有一个方法: 你仔细观察,会发现一个事情:(一下除都是整除) **1这个因子被选了n/1次,2被选了n/2次,以此类推,x被选了n/x次** 举个例子:4 1=1 2=1+2 3=1+3 4=1+2+4 1被选了4次,2被选了2次,3被选了1次,4被选了1次 所以简单来说, ### f[x]=∑n/i *(i) (1<=i<=x) 直接o(n)扫一遍,60分 ------------ 那到底怎么做呢? ## 正解:除法分块 什么叫除法分块?又该怎么写? 拿100来说,我们发现 100/51=1 100/52=1 …… 100/99=1 100/100=1 我们发现51~100那么大一段数,他们的n/i都是1,所以我们可以对其合并,将51~100这一段的值合并为1*(51+……+100),然后用等差数列求和公式O(1)求出即可 所以,简单来说 **除法分块就是把一些n/i值相等的部分合并,用等差数列公式求出** 怎么写? 还是以100为例 首先,设r=100 在r不为0的情况下循环 m=100/r=1 l=x/(m+1)+1=51 在l~r中n/i恒为m=1 所以,s+=m*(r+l)*(r-l+1)/2 然后下一个 , r = l-1 =50 代码: ``` i8 slove(i8 x) { i8 s=0; // for(int i=1;i<=x;i++) s+=x/i*i; i8 r=x; while(r) { i8 m=x/r; i8 l=x/(m+1)+1; s+=(r+l)*(r-l+1)*m/2; r=l-1; } return s; } ``` ------------ 完整代码: ```cpp #include <bits/stdc++.h> #define i8 __int128 #define ll long long using namespace std; const int R = 2e9; void print(i8 n) { if(n>9) print(n/10); putchar(n%10+48); } i8 ans; i8 slove(i8 x) { i8 s=0; // for(int i=1;i<=x;i++) s+=x/i*i; i8 r=x; while(r) { i8 m=x/r; i8 l=x/(m+1)+1; s+=(r+l)*(r-l+1)*m/2; r=l-1; } return s; } int x,y; int main() { cin>>x>>y; print(slove(y)-slove(x-1)); return 0; } ```