题解: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;
}

浙公网安备 33010602011771号