P5598 【XR-4】混乱度
前置知识
多重组合数
即题目中的混乱度,假设第 \(i\) 中元素有 \(a_i\) 个,那么其排列方式就是:
证明显然。
其有一个递推式子:
证明也是显然的。
Lucas 定理
对于素数 \(p\),我们有:
这是常见形式,但还有另外一种表现形式:
记 \(p\) 进制下 \(n = \overline{n_0n_1\dots n_k}, m = \overline{m_0m_1\dots m_k}\),其中高位如果不存在自动补零。那么我们有:
根据这个我们有推论:如果 \(\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;
}

浙公网安备 33010602011771号