貢獻思想 + 數論 + 思維(例題 Problem J. Prime Game)

首先說一下貢獻的思想:

舉個例子:已知有n個數,從a_1 ~ a_n ,求\sum _{i = 1} ^ {i = n} \sum_{j=i}^{j=n} \prod _{k=i} ^ {k=j} a_k 中所有質因數出現的個數。

假設當前數爲  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^2),避免不了求區間的情況,但如果使用求貢獻度的思維去求,可以在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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章