首页 > 其他分享 >CF840C

CF840C

时间:2022-10-29 09:11:41浏览次数:36  
标签:相同 相邻 int res 插入 CF840C mod

首先考虑相乘等于平方相当于两个数除去平方因子后相同。所以我们把所有数都除去平方因子,然后问的相当于是序列有多少排列满足相邻两项不等。为了更方便地 DP,我们对修改后的序列排序。

我们发现这个序列是可以被分成块的,满足每一块内数字相同。于是我们考虑每次插入好多个数而不止一个数。设第 \(i\) 块有 \(s_i\) 个相同的数。设 \(f(i,j)\) 表示目前插入了前 \(i\) 块,并且有 \(j\) 个位置相邻的数相同。

对于如何转移,我们考虑先枚举把第 \(i\) 块拆分成 \(k\) 个小块(这种方案数设其为 \(g(s_i,k)\),比较好弄,之后再说)。首先其内部会产生 \(s_i-k\) 对相邻的相同的数。然后我们考虑枚举有多少个小块插入到了满足左右两边数值一样(即在插入前位置相邻且数值相同),设为 \(p\),则对位置相邻且数值相同的对数的贡献为 \(-p\)。

设 \(q\) 为第 \(i\) 块前插入了多少个数(即块的大小总和),于是一共有 \(q+1\) 个空位可以插。

\[f(i,j+s_i-k-p)\gets \sum_{k}\binom{j}{p}\binom{q+1-j}{k-p}g(s_i,k)f(i-1,j) \]

我们看看 \(g\) 的转移。注意到块内的数是有顺序而言的。

\[g(i,j)\gets (i-1+j)\times g(i-1,j)+j\times g(i-1,j-1) \]

初值 \(f(0,0)=g(0,0)=1\)。

时间复杂度 \(\mathcal O(\sum s_in^2)=\mathcal O(n^3)\)。

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 305, mod = 1e9 + 7;
int n;
int a[N];
int fac[N], inv[N];
int f[N][N], g[N][N];
int s[N], cnt;

int add(int a, int b) { return a + b >= mod ? a + b - mod : a + b; }
int mul(int a, int b) { return 1ll * a * b % mod; }

int qpow(int x, int y) {
	int res = 1;
	while (y) {
		if (y & 1) res = mul(res, x);
		x = mul(x, x);
		y >>= 1;
	}
	return res;
}

void init(int maxn) {
	fac[0] = 1;
	for (int i = 1; i <= maxn; ++i) fac[i] = mul(fac[i - 1], i);
	inv[maxn] = qpow(fac[maxn], mod - 2);
	for (int i = maxn - 1; ~i; --i) inv[i] = mul(inv[i + 1], i + 1);
}

int C(int n, int m) {
	if (n < 0 || m < 0 || n < m) return 0;
	return mul(fac[n], mul(inv[n - m], inv[m]));
}

int div(int x) {
	int t = sqrt(x);
	for (int i = t; i; --i) {
		if (x % (i * i) == 0) {
			x /= i * i;
			break;
		}
	}
	return x;
}

int main() {
	init(300);
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), a[i] = div(a[i]);
	sort(a + 1, a + n + 1);
	for (int i = 1; i <= n; ++i) {
		if (a[i] != a[i - 1]) s[++cnt] = 1;
		else ++s[cnt];
	}
	g[0][0] = 1;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) g[i][j] = add(mul(i - 1 + j, g[i - 1][j]), mul(j, g[i - 1][j - 1]));
	}
	f[0][0] = 1;
	for (int i = 0, sum = 0; i < cnt; ++i, sum += s[i]) {
		for (int j = 0; j <= sum; ++j)
			for (int k = 1; k <= s[i + 1]; ++k) {
				for (int p = 0; p <= j; ++p)
					f[i + 1][j + s[i + 1] - k - p] = add(f[i + 1][j + s[i + 1] - k - p], mul(mul(C(j, p), C(sum + 1 - j, k - p)), mul(g[s[i + 1]][k], f[i][j])));
			}
	}
	printf("%d", f[cnt][0]);
	return 0;
}

标签:相同,相邻,int,res,插入,CF840C,mod
From: https://www.cnblogs.com/Kobe303/p/16838034.html

相关文章