PSequence 题解 DP

PSequence

题目描述

给定一个元素集合 SSS,求 SSS 的所有排列满足对于任意相邻两个元素 s1s_1s1​,s2s_2s2(s1−s2)(s_1−s_2)(s1s2) 不被 P 整除。保证 SSS 中任意两个元素都不相同。

输入格式

输入第一行一个数 nnn,表示集合 SSS 的大小

以下 nnn 个数

最后一个数 PPP

n
s1 s2 ... sn
P

输出格式

输出一个数,表述满足条件的排列的个数,模 123456789112345678911234567891

样例

input #1

4
1 2 3 4
3

output #1

12

数据范围及提示

对于 30%30\%30% 的数据,n≤9n\leq 9n9
对于 100%100\%100% 的数据,n≤30n\leq 30n30Si≤106S_i\leq 10^6Si106

注明

转载需经过本人同意。转载需经过本人同意。转载需经过本人同意。

解题思路

首先,若 sis_isisi+1s_{i+1}si+1 如果不符合条件,则 s1s_1s1s2s_2s2ppp 同。那么你用 CCC 数组可以记下 SSS 内所有元素的值模 ppp 等于的各个数的个数,从大到小排序。设要在原序列选 kkk 个不合法,lll 个合法的的位置插入,设 Fi,jF_{i,j}Fi,j 表示处理了前 iii 大的余数的数,有 jjj 对不合法的方案数的方案数。不难推出:
Fi,j−k+Ci−k−1+=Fi−1,j×Cjk×Cs+1−jl×Cc−1k+l−1 F_{i,j-k+C_i-k-1}+=F_{i-1,j}\times C_j^k\times C_{s+1-j}^l\times C_{c-1}^{k+l-1} Fi,jk+Cik1+=Fi1,j×Cjk×Cs+1jl×Cc1k+l1

当枚举到 CCC 数组的值为 000 时,答案就为:Fi−1,0×ΠCi!F_{i-1,0}\times \Pi C_i!Fi1,0×ΠCi!

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int Maxn = 30 + 5, Maxval = 1e6 + 5, Mod = 1234567891;
int Fac[Maxn], InFac[Maxn];
inline int Q_Pow(int x, int y) {
	int res = 1;
	while (y) {
		if (y & 1)
			res = res * x % Mod;
		x = x * x % Mod;
		y >>= 1;
	}
	return res;
}
inline int Get_C(int a, int b) {
	if (b > a || a < 0 || b < 0)
		return 0;
	return Fac[a] * InFac[b] % Mod * InFac[a - b] % Mod;
}
int n, s[Maxn], p;
map<int, int>Map;
int C[Maxval], Top, F[Maxn][Maxn];
int Answer;
inline bool Cmp(int a, int b) {
	return a > b;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	Fac[0] = 1;
	InFac[0] = 1;
	for (register int i = 1; i < Maxn; ++i) {
		Fac[i] = Fac[i - 1] * i % Mod;
		InFac[i] = InFac[i - 1] * Q_Pow(i, Mod - 2) % Mod;
	}
	cin >> n;
	for (register int i = 1; i <= n; ++i)
		cin >> s[i];
	cin >> p;
	for (register int i = 1; i <= n; ++i)
		++Map[(s[i] % p + p) % p];
	for (auto [x, y] : Map)
		C[Top++] = y;
	sort(C, C + Top, Cmp);
	F[0][C[0] - 1] = 1;
	for (register int i = 1, s = C[0]; i <= n; ++i) {
		if (C[i] == 0) {
			for (register int j = 0; j < Top; ++j)
				(F[i - 1][0] *= Fac[C[j]]) %= Mod;
			cout << F[i - 1][0] << endl;
			exit(0);
		}
		for (register int j = 0; j <= s; ++j)
			for (register int k = 0; k <= C[i]; ++k)
				for (register int l = 0; l <= C[i]; ++l)
					if (j - k + C[i] - k - l >= 0)
						(F[i][j - k + C[i] - k - l] += F[i - 1][j] * Get_C(j, k) % Mod * Get_C(s + 1 - j, l) % Mod * Get_C(C[i] - 1, k + l - 1) % Mod) %= Mod;
		s += C[i];
	}
	return 0;
}

然后你就秒了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值