Loading

P5598 【XR-4】混乱度

前置知识

多重组合数

即题目中的混乱度,假设第 \(i\) 中元素有 \(a_i\) 个,那么其排列方式就是:

\[\dbinom{n}{a_1, a_2, \dots a_m} = \dfrac{n!}{\prod a_i!} \]

证明显然。

其有一个递推式子:

\[\dbinom{n}{a_1, a_2, \dots a_m} = \dbinom{n - a_m}{a_1, a_2, \dots a_{m - 1}}\dbinom{n}{a_m} \]

证明也是显然的。

Lucas 定理

对于素数 \(p\),我们有:

\[\dbinom{n}{m} \equiv \dbinom{\lfloor n/p \rfloor}{\lfloor m/p \rfloor}\dbinom{n \bmod p}{m \bmod p} \pmod p \]

这是常见形式,但还有另外一种表现形式:
\(p\) 进制下 \(n = \overline{n_0n_1\dots n_k}, m = \overline{m_0m_1\dots m_k}\),其中高位如果不存在自动补零。那么我们有:

\[\dbinom{n}{m} \equiv \prod\limits_{i = 0}^k\dbinom{n_i}{m_i} \pmod p \]

根据这个我们有推论:如果 \(\dbinom{n + m}{m} \equiv 0 \pmod p\),则 \(n + m\)\(p\) 进制下的加法发生了进位,因为肯定是某一位的 \((n + m)_i < m_i\) 了,这说明一定有进位。

Subtask 1

考虑枚举左端点 \(l\),找到最后一个 \(r\) 满足 \(f(l, r)\) 不为 \(0\),之后直接 break 掉。我们声称随机情况下这是对的,因为对于 \(p\) 进制下一位,随机两个数 \(x, y \in [0, p)\),使得 \(x + y \le p\),即不产生进位的概率大概是 \(\frac{1}{2}\),有 \(\log_p w\) 位,则不进位的概率大约为 \(2^{-\log_p w}\)。所以 \(f(l, r)\) 不为 \(0\) 的区间只有 \(\mathcal{O}(n)\) 个,用 Lucas 定理计算多重组合数做到 \(\mathcal{O}(n \log_p w)\)

正解

考虑到什么情况下上面的做法会被卡:即 \(a_i\)\(p\) 进制下有很多 \(0\) 的情况,这样会造成很大的浪费。

然后我们发现,只要不管 \(a_i\)\(0\) 那些位的情况就对了!因为 \(a_i\)\(0\) 的位不会对多重组合数产生影响,也不会对进位产生影响,忽略即可。

然后对于每个 \(l\),每一位每次至少 \(+1\),就只会进行至多 \(p - 1\) 次加法了。于是时间复杂度变为 \(\mathcal{O}(np\log_pw)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// typedef __int128 i128;
typedef pair<int, int> pii;
const int N = 5e5 + 10, M = 10, mod = 998244353;
template<typename T>
void dbg(const T &t) { cout << t << endl; }
template<typename Type, typename... Types>
void dbg(const Type& arg, const Types&... args) {
    cout << arg << ' ';
    dbg(args...);
}
namespace Loop1st {
int n, p, C[M][M], val[65];
ll a[N];
vector<pii>vec[N];
void main() {
    cin >> n >> p;
    for (int i = 0; i < p; i++) {
        C[i][0] = C[i][i] = 1;
        for (int j = 1; j < i; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
    }
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        int d = 0;
        while (a[i]) {
            d++;
            int x = a[i] % p; a[i] /= p;
            if (x) vec[i].push_back({x, d});
        }
    }
    ll ans = 0;
    for (int l = 1; l <= n; l++) {
        memset(val, 0, sizeof val);
        ll res = 1;
        ans++;
        for (auto [x, d] : vec[l]) val[d] = x;
        for (int r = l + 1; r <= n; r++) {
            for (auto [x, d] : vec[r]) {
                val[d] += x;
                if (val[d] >= p) {
                    res = 0;
                    break;
                }
                res = res * C[val[d]][x] % p;
            }
            if (!res) break;
            ans += res;
        }
    }
    cout << ans << '\n';
}

}
int main() {
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T--) Loop1st::main();
    return 0;
}
posted @ 2026-01-09 20:45  循环一号  阅读(9)  评论(0)    收藏  举报