AtCoder - ABC 162 - E(容斥思维)

E - Sum of gcd of Tuples (Hard)

题意:

有 n 个大小为 1∼k 的数,找出n个数的所有排列中的 gcd(a1,a2,...,an) 之和。

数据范围:

2 ≤ N ≤ 10^{5}

 1 ≤ K ≤ 10^{5}

思路:

设 dp[i] = gcd(a1,a2,a3,……,an) = i 时的排列数量,所以排列中的 ai 必须是 i 的倍数,有 \frac{k}{a_{i}} (向下取整)个数,在 n 个位置随意选择。总共有(\frac{k}{a_{i}})^{n}种。
但是,这里面的排列不全是 gcd= i 的,还包含 gcd = i*2, gcd = i*3……的,所以要减去这些不符合的。如:i = 2,gcd[2] 中还包含 gcd[4], gcd[6]……。
实现:倒序枚举 i (k~1),先用快速幂求出(\frac{k}{a_{i}})^{n};再枚举 i 的倍数 j ,减去 dp[j]。 

Code:

#include
#include
#include
#include
#include
using namespace std;

#define x first
#define y second
#define int long long

const int N = 200010, INF = 0x3f3f3f3f, mod = 1e9 + 7;

typedef pairPII;

int n, k;
int dp[N];                           //dp[i]表示gcd=i的排列个数

//快速幂
int qmi(int a, int k)
{
	int res = 1;
	while (k)
	{
		if (k & 1)
			res = (res * a) % mod;
		k >>= 1;
		a = a * a % mod;
	}
	return res;
}

void solve()
{
	cin >> n >> k;
	int ans = 0;

	for (int i = k; i >= 1; i--)      //倒序枚举i
	{
		int num = k / i;              //num为小于k的i的倍数的个数
		dp[i] = qmi(num, n);

		for (int j = i + i; j <= k; j += i)       //枚举i的倍数j
			dp[i] = (dp[i] - dp[j] + mod) % mod;  //减去gcd[j],这里加mod是为了防止值为负数,因为算qmi时已经取modl了

		ans = (ans + dp[i] * i) % mod;            //计算i*gcd[i]的总和
	}
	cout << ans << endl;
}

signed main()
{
	int t = 1;
	//cin >> t;
	while (t--)
	{
		solve();
	}

	return 0;
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
吐槽:本题注意点:

注意模mod时,为了避免负数注意多加一次mod。

你可能感兴趣的:(AtCoder,算法)