首先说一下贡献的思想:
举个例子:已知有n个数,从
,求
中所有质因数出现的个数。
假设当前数为 6, 7, 5, 5, 4, 9, 9, 1, 8, 12
- 首先写出他每个数的质因数:
每个数的质因数分解 6 7 5 5 4 9 9 1 8 12 2,3 7 5 5 2 3 3 0 2 2,3
- 那么求任意区间质因数的个数,就可以转化为求解每个数在任意区间所作的贡献度。
求解贡献度 序号 1 2 3 4 5 6 7 8 9 10 ai 6 7 5 5 4 9 9 1 8 12 质因数 2,3 7 5 5 2 3 3 0 2 2,3 贡献度 (10) + (10) 9 + 9 8 + 8 + 8 7 6 + 6 + 6 + 6 5 + 5 + 5 + 5 4 0 2+2+2+2 1 + (1 + 2) (求解贡献度时要注意前面已经出现和计算过的数字不在计算,防止重复。如:质因数2的贡献,在序号为1处出现质因数为2,此时他的贡献度为:
[1, 1] , [1, 2], [1, 3] , [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [1, 9], [1, 10]
在序号为5处出现质因数2,此时他的贡献度为:
[1, 5](这个已经被计算过一次,舍去)
实际贡献度:[5, 5] , [5, 6] , [5, 7] , [5, 8] , [5, 9] , [5, 10]
[2, 5] , [2, 6] , [2, 7] , [2, 8] , [2, 9] , [2 , 10]
[3, 5] , [3, 6] , [3, 7] , [3, 8] , [3, 9] , [3, 10]
[4, 5] , [4, 6] , [4, 7] , [4, 8] , [4, 9] , [4, 10]
在序号为9处出现质因数2, 此时他的贡献度为:
实际贡献度: [9, 9] , [9, 10]
[6, 9] , [6, 10]
[7, 9] , [7, 10]
[8, 9] , [8, 10]
在序号为10处出现质因数2,此时他的贡献度为:
实际贡献度: [10, 10]
)
好处:
如果直接使用暴力的算法的话,时间复杂度为,避免不了求区间的情况,但如果使用求贡献度的思维去求,可以在O(n)的时间复杂度内完成
然后就是求解每个数的质因数分解:
可以直接用埃式筛法来得到:
const int maxn = 1e6+5; int vis[maxn]; vector<int>v[maxn]; void init(){ memset(vis, 0, sizeof(vis)); for(int i = 2; i < maxn; i++){ if(!vis[i]){ v[i].push_back(i); for(int j = 2*i; j < maxn; j += i){ vis[j] = 1; v[j].push_back(i); } } } }
然后就很好求解了。。。。。。。
一道例题:
这道题就是一道求贡献度的题,只要理解上面就很好求解了:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6+5;
int vis[maxn];
vector<int>v[maxn];
vector<int>pos[maxn];
int n;
int a[maxn];
void init(){
memset(vis, 0, sizeof(vis));
for(int i = 2; i < maxn; i++){
if(!vis[i]){
v[i].push_back(i);
for(int j = 2*i; j < maxn; j += i){
vis[j] = 1;
v[j].push_back(i);
}
}
}
}
int main()
{
init();
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
LL ans = 0;
for(int i = 1; i <= n; i++){
if(a[i] == 1) continue;
int tmpnum = v[a[i]].size();
for(int j = 0; j < tmpnum; j++){
int now = v[a[i]][j];
if(pos[now].size() == 0){
ans += 1LL * (n - i + 1) * i;
pos[now].push_back(i);
}
else{
vector<int>::iterator it = pos[now].end(); it--;
int tmppos = *it;
ans += 1LL * (n - i + 1) * (i - tmppos);
pos[now].push_back(i);
}
}
}
printf("%lld\n", ans);
return 0;
}