魄罗饼干的魅力

前言

最近状态很不对劲啊,总是莫名其妙忘掉一些东西,急需提升状态!

显然这就是水题解的理由。

题目

魄罗饼干大甩卖!

\(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;
}
posted @ 2022-01-25 16:38  皮皮刘  阅读(53)  评论(0编辑  收藏  举报