首先說一下貢獻的思想:
舉個例子:已知有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;
}