题解:CF2064E Mycraft Sand Sort

CF2064E Mycraft Sand Sort

Description

给定一个排列 \(p\) 和序列 \(c\),长度都为 \(n(\le 2\times 10^5)\),表示在一个二维的有重力的环境中,从上往下第 \(i\) 行,从左往右排列着 \(p_i\) 个颜色为 \(c_i\) 的受重力的大小相等的方块。

这些方块受到重力影响,会往下掉落,直到下面有支撑,第 \(n\) 行的下面是地面。

示例图片

请求出有多少对不同的 \((p', c')\),使得掉落后的结果与给定的 \((p, c)\) 相同。对 \(998244353\) 取模。

Solution

发现第一列的颜色不会改变,所以 \(c' = c\),我们只需考虑有多少不同的 \(p'\)

若要产生不同的 \(p'\),相当于对 \(p\) 做若干次交换的操作。

假设交换 \(p_i\)\(p_j\)\((i<j)\),那么必须满足 \(c_i = c_j\),并且 \([i,j]\) 中不存在 \(p_k > \min(p_i,p_j)\)

那怎么统计答案呢?

\(p\) 排序后为 \(a\),显然 \(a_1\) 只能和它 \(p\) 左右相邻且颜色相同的交换,交换的方案数就是这个相同相邻颜色段的大小。那如果我们把 \(a_1\)\(p\) 中删去,并把 \(p\) 的左右两块合并,发现对于 \(a_2\) 来说也是这么个情况。依此类推。

那么我们只需要维护连续相同颜色段,并支持删除,合并。DSU + 链表 容易做到这一点。

利用乘法原理,把每次交换的方案数相乘即可。

Code

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;

const int N = 2e5 + 5, INF = 1e9, MOD = 998244353;

int n, p[N], c[N], pos[N];
int l[N], r[N];

struct DSU {
    int n, fa[N], siz[N];

    void init(int _n) {
        n = _n;
        for (int i = 1; i <= n; i++) {
            fa[i] = i;
            siz[i] = 1;
        }
    }

    int find(int x) { return (fa[x] == x ? x : fa[x] = find(fa[x])); }

    void merge(int x, int y) {
        int fx = find(x), fy = find(y);
        if (fx == fy) return;
        if (siz[fx] > siz[fy]) swap(fx, fy);
        fa[fx] = fy;
        siz[fy] += siz[fx];
    }

    bool is_merge(int x, int y) { return find(x) == find(y); }
} dsu;

void erase(int x) {
    l[r[x]] = l[x];
    r[l[x]] = r[x];
}

void Solve() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> p[i];
        pos[p[i]] = i;
    }
    dsu.init(n);
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
        if (c[i] == c[i - 1]) dsu.merge(i, i - 1);
        l[i] = i - 1;
        r[i] = i + 1;
    }
    int ans = 1;
    for (int i = 1; i <= n; i++) {
        int cur = pos[i];
        int f = dsu.find(cur);
        ans = (LL)ans * dsu.siz[f] % MOD;
        erase(cur);
        if (!(--dsu.siz[f])) {
            if (l[cur] && r[cur] <= n && c[l[cur]] == c[r[cur]]) dsu.merge(l[cur], r[cur]);
        }
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T;
    cin >> T;
    while (T--) Solve();
    return 0;
}
posted @ 2025-02-19 16:36  chenwenmo  阅读(23)  评论(0)    收藏  举报