ARC223B Valid Arrays by K-Divisible Swaps

个人记录第二十四篇。

题意

给你一个长为 \(n\) 的正整数序列 \(A=\{a_1,a_2,a_3,\dots,a_n\}\) 和一个正整数 \(K\)。你可以对 \(A\) 进行任意次(可以为 \(0\) 次)操作,具体如下:

  • 选择 \(i\in[1,n-1]\),如果 \(a_i+a_{i+1}\) 可以被 \(K\) 整除,则将其交换。

问你在若干次操作后可以得到的 \(A\) 的数量,答案对 \(998244353\) 取模。

解法

显然,\(a_i\)\(a_{i+1}\) 能够交换,当且仅当 \((a_i+a_{i+1})\mod K=0\)。令 \(r_i=a_i\mod K\),那么可以得到 \(r_i+r_{i+1}\equiv0\pmod K\)

所以我们考虑通过这类余数进行分段,满足 \(r_i+r_{i+1}\equiv0\pmod K\) 的数分为一段来计算。

那么对于一般情况,它们必须满足相邻两数余数分别为 \(r_i\)\(K-r_i\) 才可交换,否则不可以。所以对于同一个余数 \(r_i\),它们对应的数的相对位置是不会改变的。设余数为 \(r_i\) 的数有 \(x\) 个,余数为 \(K-r_i\) 的数有 \(y\) 个,那么这就相当于往一个长为 \(x+y\) 的序列中选择 \(x\) 个放 \(r_i\),剩下的放 \(K-r_i\),则这一段的贡献是

\[\binom{x+y}{x} \]

但是我们发现有两类数比较特殊,它们对应的 \(r_i\) 等于 \(0\)\(K/2\)(如果 \(K\) 是偶数)。此时它们在同一段内任意交换顺序也能够满足条件。设这一段长度为 \(len\),这一段中值 \(v\) 出现了 \(cnt_v\) 次,那么这一段的贡献就是多重集全排列数

\[\frac{len!}{\prod_v cnt_v!} \]

最终答案就是每一段的方案数相乘。

时间复杂度 \(O(T\sum n)\)

Code
int n, k;
int a[N];
mo fac[N], inv[N];//mo 是手写的自动取模类

inline void init () {
    fac[0] = 1;
    for (int i = 1; i <= N - 15; ++ i) fac[i] = fac[i - 1] * i;
    inv[N - 15] = ~fac[N - 15];//~ 是求逆元
    for (int i = N - 15; i >= 1; -- i) inv[i - 1] = inv[i] * i;
}

inline int get (int x) {
    int r = (x % k + k) % k;
    if (r == 0) return 0;
    if (k % 2 == 0 && r == k >> 1) return k >> 1;
    return min (r, k - r);
}//给每个数编号来分段

inline mo C (int n, int m) {
    if (n < m || m < 0) return 0;
    return fac[n] * inv[m] * inv[n - m];
}

inline void solve () {
    cin >> n >> k;
    for (int i = 1; i <= n; ++ i) cin >> a[i];
    mo ans = 1;
    for (int i = 1; i <= n; ) {
        int j = i;
        int id = get (a[i] % k);
        while (j <= n && get (a[j] % k) == id) ++ j;
        int len = j - i;
        if (!id || (k % 2 == 0 && id == k >> 1)) {
            unordered_map <i64, int> mp;
            for (int l = i; l < j; ++ l) ++ mp[a[l]];
            mo x = fac[len];
            for (auto p : mp) x *= inv[p.second];
            ans *= x;
        }//余数为 0 或 K/2
        else {
            int cnt1 = 0, cnt2 = 0;
            for (int l = i; l < j; ++ l) {
                int r = (a[l] % k + k) % k;
                if (r == id) ++ cnt1;
                else cnt2 ++;
            }
            if (cnt1 && cnt2) ans *= C (cnt1 + cnt2, cnt1);
        }//一般情况
        i = j;
    }
    cout << ans << endl;
}
posted @ 2026-06-29 07:58  XXh_Laoxu  阅读(15)  评论(0)    收藏  举报

转载请注明出处!


#页面摧毁游戏#
使用【上下左右】控制飞行器的运动
使用【空格】发射导弹
点击开始摧毁