贡献思想 + 数论 + 思维(例题 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;
}

 

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