• 魄罗饼干的魅力


    前言

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

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

    题目

    魄罗饼干大甩卖!

    \(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;
    }
    
  • 相关阅读:
    Android studio快捷键总结
    汇编寄存器(内存访问)基础知识之四----栈
    汇编寄存器(内存访问)基础知识之三---mov指令
    android studio学习之一
    ASCII值对照表
    chrome 优秀的插件推荐
    android基础知识之一
    汇编基础知识之二debug的使用
    计算机专业课系列之三:进程和线程
    计算机专业课系列之二:程序的机器表示(汇编)
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15843566.html
Copyright © 2020-2023  润新知