bzoj 4305 数列的GCD 容斥原理

题面

题目传送门

解法

  • 显然,恰好有 k k 个不相同的位置就意味着恰好有 n k n-k 个位置的数是相同的,不妨令 k = n k k=n-k ,求恰好有 k k 个位置相同且满足条件的方案数。
  • 可以考虑这样一个容斥:令 s [ i ] s[i] 表示有因子 i i 的数的个数,那么可以得到 f [ i ] = i j μ ( j i ) ( s [ j ] k ) ( m j 1 ) s [ j ] k ( m j ) n s [ j ] f[i]=\sum_{i|j}\mu(\frac{j}{i}){s[j]\choose k}(\lfloor\frac{m}{j}\rfloor-1)^{s[j]-k}(\lfloor\frac{m}{j}\rfloor)^{n-s[j]}
  • 式子的含义应该比较简单,考虑先用 s [ j ] s[j] 个含有因子 j j 的填满 k k 个相同的位置,那么剩下的 s [ j ] k s[j]-k 个位置中每一个都有 m j 1 \lfloor\frac{m}{j}\rfloor-1 种选择,剩下 n s [ j ] n-s[j] 个位置中每一个都有 m j \lfloor\frac{m}{j}\rfloor 种选择。
  • s s f f 都可以通过 O ( m log m ) O(m\log m) 的时间复杂度来实现
  • 时间复杂度: O ( m log m ) O(m\log m)
  • 反正这种题想怎么玩就怎么玩,快乐就行

代码

#include <bits/stdc++.h>
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x < y ? x : y;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
const int N = 300010, Mod = 1e9 + 7;
int a[N], f[N], s[N], p[N], mu[N], tx[N], ty[N], fl[N], fac[N], inv[N], Inv[N];
void add(int &x, int y) {x += y; if (x >= Mod) x -= Mod;}
int C(int n, int m) {return 1ll * fac[n] * Inv[m] % Mod * Inv[n - m] % Mod;}
int Pow(int x, int y) {
	if (y < 0) return 0; int ret = 1;
	for (; y; y >>= 1, x = 1ll * x * x % Mod)
		if (y & 1) ret = 1ll * ret * x % Mod;
	return ret;
}
int main() {
	int n, m, K; read(n), read(m), read(K); K = n - K;
	for (int i = 1; i <= n; i++) {int x; read(x); a[x]++;}
	for (int i = m; i; i--)
		for (int j = i; j <= m; j += i)
			s[i] += a[j];
	fac[0] = fac[1] = inv[1] = Inv[0] = Inv[1] = 1;
	for (int i = 2; i <= n; i++) {
		inv[i] = 1ll * (Mod - Mod / i) * inv[Mod % i] % Mod;
		Inv[i] = 1ll * Inv[i - 1] * inv[i] % Mod;
		fac[i] = 1ll * fac[i - 1] * i % Mod;
	}
	for (int i = 1; i <= m; i++)
		tx[i] = Pow(m / i - 1, s[i] - K), ty[i] = Pow(m / i, n - s[i]);
	int len = 0; mu[1] = 1;
	for (int i = 2; i <= m; i++) {
		if (!fl[i]) p[++len] = i, mu[i] = -1;
		for (int j = 1; j <= len && i * p[j] <= m; j++) {
			int k = i * p[j]; fl[k] = 1;
			if (i % p[j] == 0) {mu[k] = 0; continue;}
			mu[k] = -mu[i];
		}
	}
	for (int i = 1; i <= m; i++) add(mu[i], Mod);
	for (int i = 1; i <= m; i++)
		for (int j = i; j <= m; j += i)
			if (s[j] >= K) add(f[i], 1ll * mu[j / i] * C(s[j], K) % Mod * tx[j] % Mod * ty[j] % Mod);
	for (int i = 1; i <= m; i++) cout << f[i] << ' '; cout << "\n";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/emmmmmmmmm/article/details/88094289