• [题解] 20212022 ICPC, NERC, Northern Eurasia Onsite F Fancy Stack


    给你一个有 \(n\) 个数的单调不降的序列 \(a\),让这个序列重新排列为 \(b\)。使得对于所有偶数位有 \(b_2 < b_4 < ... < b_{n-2} < b_n\),对于所有奇数位 \(i\)\(b_i < b_{i-1}, b_{i} < b_{i+1}\),注意是严格小于。

    求合法 \(b\) 排列的方案数。对 \(998244353\) 取模,保证 \(n\) 是偶数。

    赛时想了很长时间都不会,可能是我傻逼了。lzy 写了写 WA6,可惜最后没调出来。

    下面的做法是翻译的题解。

    先考虑所有数都不相同的情况。

    \(f_{i,j}\) 表示考虑了最大的 \(i\) 个数,有 \(j\) 个数放在了偶数位上。

    现在考虑第 \(i+1\) 个数。

    • 如果放在偶数位上 \(f_{i+1,j+1} \gets f_{i,j}\)
    • 如果放在奇数位上 \(f_{i+1,j} \gets f_{i,j} \times (\max(j-1,0) + [j=\frac{n}{2}] - (i-j))\)

    当然这是不相同的情况。

    如果存在数相同的话,可以把相同的数合并成一个,转移的时候枚举几个数作为偶数位,然后剩下的几个数放在奇数位上的方案数直接用组合数计算即可。 上面的这个是错的,因为要保证 \(b_{2} < b_4 < b_6 < ... < b_n\),所以相同的数中只能有 \(0\)\(1\) 个数放在偶数位,不过算贡献的时候确实需要转化成组合数统计。

    总复杂度 \(\mathcal O(n^2)\)

    理论存在,开写!

    现在是 15:11 我看我什么时候写完。

    现在是 16:45 我写完了,调了 \(\infty\) 时间的缘故是多测没清干净 /px

    #include<bits/stdc++.h>
    #define LL long long
    #define int long long
    #define orz cout << "tyy YYDS!!!\n"
    using namespace std;
    const int MAXN = 5e3 + 10;
    const int INF = 1e9 + 7;
    const int mod = 998244353;
    
    int n, m;
    int fac[MAXN], inv[MAXN];
    int a[MAXN], b[MAXN], cnt[MAXN], s[MAXN];
    int f[MAXN][MAXN];
    
    int read() {
    	int s = 0, f = 0; char ch = getchar();
    	while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
    	while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    	return f ? -s : s;
    }
    
    void Init(int M) {
    	fac[0] = fac[1] = inv[0] = inv[1] = 1;
    	for(int i = 2; i <= M; ++i) {
    		fac[i] = fac[i - 1] * i % mod;
    		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    	}
    	for(int i = 2; i <= M; ++i) inv[i] = inv[i - 1] * inv[i] % mod;
    }
    
    int C(int n, int m) { return (n < 0 || n < m) ? 0 : fac[n] * inv[m] % mod * inv[n - m] % mod; }
    
    void Main() {
    	n = read(), m = 0;
    	for(int i = 0; i <= n + 1; ++i) a[i] = b[i] = cnt[i] = s[i] = 0;
    	for(int i = 1; i <= n; ++i) a[i] = read();
    	for(int i = 1; i <= n; ++i) {
    		if(a[i] != a[i - 1]) b[++m] = a[i], cnt[m] = 1;
    		else cnt[m] ++;
    	}
    	for(int i = m; i >= 1; --i) s[i] = s[i + 1] + cnt[i];
    	for(int i = 0; i <= n + 1; ++i) for(int j = 0; j <= n + 1; ++j) f[i][j] = 0;
    	f[m + 1][0] = 1;
    	for(int i = m + 1, M = n / 2; i > 1; --i) { // 枚举已经填了哪一位,接下来要填的这一位是 i-1 
    		for(int j = 0; j <= M; ++j) { // 枚举已经放了几个偶数位 
    			for(int k = cnt[i - 1] - 1; k <= cnt[i - 1]; ++k) { // 枚举拿几个放在奇数位,奇数位应该放 (max(j-1,0)+(j==M)) 个,已经放了 (s[i] - j) 个 
    				if(j + cnt[i - 1] - k <= M) {
                        f[i - 1][j + (cnt[i - 1] - k)] = (f[i - 1][j + (cnt[i - 1] - k)] + f[i][j] * C(max(j - 1, 0ll) + (j == M) - (s[i] - j), k) % mod) % mod;
                    }
    			}
    		}
    	}
    	printf("%lld\n", f[1][n/2]);
    }
    
    signed main() {
    	Init(5000);
    	int T = read();
    	while(T--) Main();
    	return 0;
    }
    
  • 相关阅读:
    ios每日一发--Leanclude数据云存储以及登录 注册账户
    ios每日一发--仿侧边抽屉效果
    ios-自定义alertView提示框
    ios
    UIStepper 缩放:UI的使用
    UIButton 关灯小实验
    将一个字典内的内value转换为集合:返回一个数组,此数组中包含输入字典的键值对中的数组的所有元素(为NSArray添加category)
    为集合排序的三个方法
    NSMutableDictionary
    头文件导入方式
  • 原文地址:https://www.cnblogs.com/Silymtics/p/16145477.html
Copyright © 2020-2023  润新知