前言
最近状态很不对劲啊,总是莫名其妙忘掉一些东西,急需提升状态!
显然这就是水题解的理由。
题目
魄罗饼干大甩卖!
有 \(n\) 个人在排队购买魄罗饼干,这些人要么是皮城人,要么是祖安人(无贬义)。
-
祖安人的特点是珍惜时间,所以他们希望自己不晚于 \(|a_i|\) 排到队。
-
皮城人的特点是喜欢装杯,所以他们希望自己不早于 \(|a_i|\) 排到队。
其中 \(a_i>0\) 表示祖安人,\(a_i<0\) 表示皮城人。
现在卖饼干的商人想要知道有多少种排队方式满足条件,你能帮帮他吗?
\(1\le n\le 5000;1\le |a_i|\le n.\)
样例输入
4
2 -2 -3 4
样例输出
6
讲解
这类要求在某时间之前做事的条件用代价延时计算即可轻松解决。计数同理。
因此这道题可以顺序解决,直接令 \(dp_{i,j}\) 表示前 \(i\) 个位置有 \(j\) 个皮城人的方案数,那么对于皮城人,我们有:
\[dp_{i,j}\leftarrow dp_{i-1,j-1}\times (s_1-j)+dp_{i-1,j}
\]
其中 \(s_1\) 表示到目前位置,有多少个皮城人能够在此处排队。
对于祖安人,我们之前说了,计数延后计算,于是我们在 \(i\) 这个位置只需要满足 \(|a_k|=i\) 的祖安人条件即可,令 \(|a_k|=i\) 的祖安人有 \(cnt2_{i}\) 个,\(|a_k|<i\) 的祖安人有 \(s_2\) 个,则有:
\[dp_{i,j}\leftarrow dp_{i,j}\times A(i-j-s2,cnt2_i)
\]
时间复杂度 \(O(n^2)\)。
代码
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 5005;
const int MOD = 1e9+7;
int n;
LL Read()
{
LL x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
int fac[MAXN],ifac[MAXN];
int qpow(int x,int y)
{
int ret = 1;
while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
return ret;
}
void init(int x)
{
fac[0] = ifac[0] = 1;
for(int i = 1;i <= x;++ i) fac[i] = 1ll * fac[i-1] * i % MOD;
ifac[x] = qpow(fac[x],MOD-2);
for(int i = x-1;i >= 1;-- i) ifac[i] = ifac[i+1] * (i+1ll) % MOD;
}
LL A(int x,int y)
{
if(x < y || y < 0) return 0;
return 1ll * fac[x] * ifac[x-y] % MOD;
}
int cnt1[MAXN],cnt2[MAXN],dp[2][MAXN];
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
init(n = Read());
for(int i = 1,v;i <= n;++ i)
{
v = Read();
if(v < 0) ++cnt1[-v];
else ++cnt2[v];
}
int s1 = 0,s2 = 0,now = 0;
dp[0][0] = 1;
for(int i = 1;i <= n;++ i)
{
bool to = now ^ 1;
memset(dp[to],0,sizeof(dp[to]));
s1 += cnt1[i];
for(int j = 0;j < i;++ j)
{
if(j <= s1) dp[to][j+1] = (dp[to][j+1] + 1ll * dp[now][j] * (s1-j)) % MOD;
dp[to][j] = (dp[to][j] + dp[now][j]) % MOD;
}
if(cnt2[i]) for(int j = 0;j <= i;++ j) dp[to][j] = A(i-j-s2,cnt2[i]) * dp[to][j] % MOD;
s2 += cnt2[i];
now ^= 1;
}
Put(dp[now][s1],'\n');
return 0;
}