Loading

AT_jsc2019_qual_c Cell Inversion

算法

场上也是把所有需要的性质全部都推出来了, 但是计数类型的底子太差, 直接也是没把答案式子表示出来啊

容易的, 我们可以知道, 对于一个长度为 \(n\) 的序列, 其中每一个 \([l_i, r_i]\) 确定, 那么不管怎样排列, 最终都是合法的
我们还可以知道, 如果每一个点, 作为左端点还是右端点, 那么不管左右端点如何组合, 最终也都是合法的

无论怎样, 就算想的是歪解, 也会在操作过程中发现这两个性质, 这是简单的

那么考虑接下来怎么做, 考场上我的想法是, 令 \(1\) 表示当前点为左端点, \(-1\) 表示当前点为右端点, 那么这个点是白色还是黑色相当于限制了前缀的奇偶性, 特别的, 如果当前点是 \(-1\) , 那么这个点的前缀和不计算这个 \(-1\) , 原因是显然的, 就算这是右端点, 你也还是被取反了一次是吧

那么对于每个点, 取 \(1\)\(-1\) 会产生奇偶性的变化, 显然的, 这样可以确定每个点应该填 \(1\) 还是 \(-1\) , 这以后我的做法就偏差很多了, 当时我在想记忆化搜索??? 本质上是因为没有考虑到这里可以确定 \(1\)\(-1\) 的取值

那么好, 现在我们知道每个点应当取左端点还有右端点, 那么怎么计算一共有多少种配对方式呢?

然后场上的我就开始 \(\rm{dfs}\) 了, 糖

显然的, 我们可以用组合数学的方法来解决这个问题,

\(Cntl_i\) 表示 \([0, i]\) 中左端点的出现次数, \(Cntr_i\) 表示 \([0, i]\) 中右端点的出现次数, \(pos_i\) 表示第 \(i\) 个右端点的位置
对于每一个右端点, 我们显然可以取 \({Cntl}_{i - 1} - {Cntr}_{i - 1}\) 种左端点

那么显然的, 最终的答案式子可以写成:

\[n! \times \prod_{i = 1}^{{Cntr}_{n}} \left(Cntl_{pos_i - 1} - i + 1\right) \]

至此, 可以通过本题

代码

#include <bits/stdc++.h>
const int MOD = 1e9 + 7;
const int MAXN = 1e5 + 20;

int n;

int main()
{
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        std::string Col;
        std::cin >> Col;

        long long ans = 1;
        for (int i = 1; i <= n; i++)
            ans = ans * i % MOD;

        std::vector<int> a(2 * MAXN);
        for (int i = 0; i < 2 * n; i++)
            a[i] = (Col[i] == (i % 2 ? 'B' : 'W'));
        int sum = 0;
        for (int i = 0; i < 2 * n; i++) {
            if (!a[i])
                ++sum;
            else {
                ans = ans * sum % MOD;
                --sum;
            }
        }
        if (sum != 0) // 左右端点数量不等
            ans = 0;
        printf("%lld\n", ans);
    }
}

总结

推了很多, 卡在一个地方, 可以考虑统筹所有信息再来一遍

组合数学要多练

posted @ 2024-12-04 15:14  Yorg  阅读(23)  评论(0)    收藏  举报