AT_abc425_e 题解
前言
时隔1个月,我又来写题解了~~~~~。
这个题目其实不是很难,挺简单的,考场上思路方向错了。
题目传送门:
题面:
你会获得一个正整数\(N\),和一个长度为\(N\)的正整数数组:\(C = (C_1,C_2,...,C_N)\)
你需要求出的是:所有满足以下条件的数组的个数模上给定的正整数\(M\)
-
数组中所有的数字均为\(1\)到\(N\)中的一个数。
-
对于\(i = 1,2,...,N\),数字\(i\)在数组中出现正好\(C_i\)次。
每次会给\(T\)组数据,你需要把每个数据的答案都求出来,\(M\)在这\(T\)组数据中都是一样的。 -
\(1 \leq T \leq 10^5\)
-
\(2 \leq M \leq 10^9\)
-
\(1 \leq N\)
-
\(1 \leq C_i\)
-
\(\displaystyle{\sum_{i=1}^{N} C_i} \leq 5000\)
-
所有的\(T\)组数据中,所有\(N\)的和不超过\(3 \times 10^5\)
-
所有的输入都是整数。
题解
首先们可以把题目稍微简化一下。
那么这道题就是把\(C_1个1,C_2个2,C_3个3,...,C_N个N\)全部拼起来的方案数,总长度是\(\displaystyle{\sum_{i=1}^{N} C_i}\)。
我们给\(C\)求一个前缀和\(S\),即$S_i = \displaystyle{\sum_{j=1}^{i} C_j} $
那么总长度就是\(S_N\)。
接下来算拼起来的方案数,可以从简单题考虑到难题。
简单题:
有15个球,除了颜色外都相同,其中有5个红球,6个蓝球,4个绿球请问有多少种排法。
这道题的做法很简单:先算15个球中选5个红球,再从剩下的10个球中选6个蓝球,最后在5个球种选4个绿球。
列式就是:\(C_{15}^5 \times C_{(15-5)}^6 \times C_{(15-5-6)}^4\)
那么类比到这道题
可以看出答案就是 \(\displaystyle{\prod_{i=1}^{N} \Large{C}_{\small{(S_N-S_{i-1})}}^{\small{C_i}}}\)
\((S_N-S_{i-1})\)就是算取\(C_i\)个数字(也就是上面的球)前剩下多少。
那么这道题就基本解决了。。。。。等一下!如果我们直接每次算\(\Large{C}_{\small{(S_N-S_{i-1})}}^{\small{C_i}}\)会时间爆掉,怎么办呢?
我们可以提前预处理出每一个\(C_i^j\)来,因为\((S_N-S_{i-1})\)和\(C_i\)不超过5000,所以我们可以用\(5000^2\)的时间把\(C_i^j\)预处理出来。而且由于\(M\)在所有的组中是一样的,所以可以直接在输入后去预处理(预处理用杨辉三角),这样就可以不爆时间了。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int c[300001];
int C[5001][5001];
int s[300001];
signed main()
{
int t, m;
cin >> t >> m;
C[0][0] = 1;
for (int i = 1; i <= 5000; i++) // 预处理
{
C[i][0] = 1;
for (int j = 1; j <= i; j++)
{
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % m; // 杨辉三角
}
}
while (t--)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> c[i];
s[i] = s[i - 1] + c[i]; // 求前缀和
}
int ans = 1;
for (int i = 1; i <= n; i++)
{
ans = ans * C[s[n] - s[i - 1]][c[i]] % m; // 按照公式,求答案。
}
cout << ans << endl;
}
return 0;
}
完结撒花~~~~

浙公网安备 33010602011771号