Codeforces 1666F. Fancy Stack
作者:互联网
传送门
\(\texttt{Difficulty:2200}\)
题目大意
一个长为 \(n(1\le n\le5000,n\) 为偶数 \()\) 的升序序列 \(a(1\le a_i\le n)\) 。将 \(a\) 中的元素重新排布,组成序列 \(b\) ,使得 \(b_1 < b_2 > b_3 < b_4 > \ldots > b_{n-1} < b_n\) 并且 \(b_2 < b_4 < b_6 < \ldots < b_n\) 。求有多少种不同的 \(b\) 序列,对 \(998244353\) 取模。
思路
考虑 \(dp\) 。我们先对原序列中元素去个重 ,去重之后原序列的大小记为 \(m\) 。\(cnt_i\) 记录第 \(i\) 大的元素的个数。我们考虑从大往小将 \(a\) 序列中元素加入 \(b\) 的过程,考虑当前元素如果可以放在偶数位 \(i\) ,那么所有 \(>i\) 的偶数位以及 \(i-1,i+1\) 两个位置都不能够有元素否则一定不会合法,放在奇数位 \(j\) 时则要求其两侧的偶数位都已经被放置,于是我们设计状态 \(f_{i,j}\) 表示第 \(i\) 到第 \(m\) 大的元素都已经被放入,且恰好 \(j\) 个被放在偶数位上的合法方案数。此时偶数位放置了那些是确定的,那么可供自由放置的奇数位也就确定了,对于重复元素的情况下,新的元素如果放偶数位则只有 \(1\) 个可以放偶数为,此外注意新加入时无论如何可放置的奇数位个数在操作完成前不会增加,记 \(s\) 为 \(cnt\) 的后缀和, \(can_j\) 为 \(j\) 个偶数位被放置时可放置的奇数位数量,注意处理边界,于是可以推导出转移方程:
\[f_{i-1,j+1}=f_{i-1,j+1}+cnt_{i-1}\cdot f_{i,j}\cdot\frac{(can_j-s_i+j)!}{(can_j-s_i+j-cnt_{i-1}+1)!} \]\[f_{i-1,j}=f_{i-1,j}+f_{i,j}\cdot\frac{(can_j-s_i+j)!}{(can_j-s_i+j-cnt_{i-1})!} \]初始设 \(f_{m+1,0}=1\) ,其余为 \(0\) ,答案还要去重,结果为 \(\frac{f_{1,\frac{n}{2}}}{\prod_{i=1}^m cnt_i!}\) ,复杂度 \(O(n^2)\) 。
代码
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 5010;
LL T, N, A[maxn], S[maxn], cnt[maxn], F[maxn][maxn];
LL fact[maxn], invfact[maxn];
LL qpow(LL a, LL x, LL m)
{
LL ans = 1;
while (x)
{
if (x & 1)
ans = ans * a % m;
x >>= 1;
a = a * a % m;
}
return ans % m;
}
void fact_init(LL n, LL m)
{
fact[0] = fact[1] = 1;
for (LL i = 2; i <= n; i++)
fact[i] = fact[i - 1] * i % m;
invfact[n] = qpow(fact[n], m - 2, m);;
for (LL i = n; i > 0; i--)
invfact[i - 1] = invfact[i] * i % m;
}
LL f(LL x)
{
if (x < 0)
return 0;
return fact[x];
}
LL invf(LL x)
{
if (x < 0)
return 0;
return invfact[x];
}
LL get(LL j)
{
if (j == N / 2)
return j;
else
return max(0LL, j - 1);
}
void solve()
{
int M = 0;
for (int i = 1; i <= N; i++)
{
if (cnt[i] > 0)
cnt[++M] = cnt[i];
}
for (int i = 1; i <= M + 1; i++)
S[i] = 0;
for (int i = M; i >= 1; i--)
S[i] = S[i + 1] + cnt[i];
for (int i = M + 1; i >= 0; i--)
{
for (int j = 0; j <= N / 2; j++)
F[i][j] = 0;
}
F[M + 1][0] = 1;
for (int i = M + 1; i >= 2; i--)
{
for (int j = 0; j <= N / 2; j++)
{
LL nowcan = get(j) - (S[i] - j);
F[i - 1][j + 1] = (F[i - 1][j + 1] + cnt[i - 1] * F[i][j] % mod * f(nowcan) % mod * invf(nowcan - (cnt[i - 1] - 1)) % mod) % mod;
F[i - 1][j] = (F[i - 1][j] + F[i][j] % mod * f(nowcan) % mod * invf(nowcan - cnt[i - 1]) % mod) % mod;
}
}
for (int i = 1; i <= M; i++)
F[1][N / 2] = F[1][N / 2] * invfact[cnt[i]] % mod;
cout << F[1][N / 2] << endl;
}
int main()
{
IOS;
fact_init(5005, mod);
cin >> T;
while (T--)
{
cin >> N;
for (int i = 1; i <= N + 5; i++)
cnt[i] = 0;
for (int i = 1; i <= N; i++)
cin >> A[i], cnt[A[i]]++;
solve();
}
return 0;
}
标签:cnt,return,int,LL,Codeforces,Fancy,1666F,maxn,define 来源: https://www.cnblogs.com/Prgl/p/16478101.html