2025 暑期 mx 集训 7.29
T1
https://www.mxoj.net/problem/P110073?contestId=82
题意
给你 \(x,p\),求 \((x, y)\) 的数量满足 \(\gcd(x, y)^p = \text{lcm}(x, y)\)。
\(x \leq 10^9, 1\leq p \leq 10\)。
Solution
考虑 \(\gcd(x, y) = p_1^{\min(k_{1,0}, k_{1,1})} \times p_2^{\min(k_{2,0}, k_{2,1})} \times \cdots \times p_m^{\min(k_{m,0}, k_{m,1})}\)。
然后 \(\text{lcm}(x, y) = p_1^{\max(k_{1,0}, k_{1,1})} \times p_2^{\max(k_{2,0}, k_{2,1})} \times \cdots \times p_m^{\max(k_{m,0}, k_{m,1})}\)。
那么就是对 \(x\) 进行质因数分解,然后考虑每个质因子是作为 \(\min\) 还是 \(\max\),作为 \(\min\) 的话,那么另一个一定是 \(k_i^p\)。
作为 \(\max\) 的话必须得满足 \(k_i \equiv 0 \bmod p\),那只需要把每个的方案乘起来即可。
不过 \(p = 1\) 的情况要特判掉。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10, P = 998244353;
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int x, p; cin >> x >> p;
vector<pair<int, int>> q;
if (p == 1) {
cout << 1;
return 0;
}
for (int i = 2; i * i <= x; i++) {
int c = 0;
while (!(x % i)) x /= i, c++;
if (c) q.emplace_back(i, c);
}
if (x) q.emplace_back(x, 1);
int ans = 1;
for (auto v : q) {
int y = v.second;
ans = 1ll * ans * (1 + !(y % p)) % P;
}
cout << ans;
return 0;
}
T2
https://www.mxoj.net/problem/P110074?contestId=82
题意
给你 \(n\) 个数 \(a_i\),你要选择一个长为 \(m\) 的首尾相接的序列 \(b\),使得 \(\max_{i = 1}^m (b_i + b_{i \bmod m} + 1)\) 最小。
\(n\leq 2\times 10^5\)。
Solution
因为最大值最小,考虑二分答案。
设答案为 \(mid\),那么考虑我们选上最小的那个数一定不劣,所以直接从最小的那个数开始选。
然后我们就分别往左右扩展,能选就选,选不了就留下 \(\min(last, a_i)\),然后删掉一个。
我们维护删掉的数的个数,如果留下的数 \(\ge m\) 那就合法,然后调整二分边界即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
int n, m, a[N], p;
bool chk(int x)
{
int c = 0, la = 0;
for (int i = p; i < n; i++) {
if (a[i] + la > x) {
c++;
la = min(la, a[i]);
} else la = a[i];
}
int k = la;
la = a[p];
for (int i = p - 1; i >= 0; i--) {
if (a[i] + la > x) {
c++;
la = min(la, a[i]);
} else la = a[i];
}
if (la + k > x) c++;
return c <= n - m;
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n >> m; int mn = inf;
for (int i = 0; i < n; i++) cin >> a[i], mn = min(mn, a[i]);
for (int i = 0; i < n; i++) if (a[i] == mn) p = i;
int l = 0, r = 1e9, res = 0;
while (l <= r) {
int mid = (l + r) / 2;
if (chk(mid)) r = (res = mid) - 1;
else l = mid + 1;
}
cout << res;
return 0;
}
T3
https://www.mxoj.net/problem/P110075?contestId=82
题意
给你 \(n\) 个数,求有多少种划分方案使得所有划分出来的区间的 \(\text{mex}\) 相等,对 \(998244353\) 取模。
\(n\leq 3\times 10^5\)。
Solution
重要性质:考虑所有划分区间的 \(\text{mex} = k\),那么 \(k\) 在整个序列中一定未出现,那么整个序列的 \(\text{mex}\) 也应该 $ = k$。
所以我们先求出整个序列的,那么我们就知道每个区间的 \(\text{mex}\) 是多少了。
那设 \(f_i\) 表示以 \(i\) 为结尾的方案,\(k\) 为整个序列的 \(\text{mex}\),转移有:
初始化 \(f_0 = 1\)。
然后考虑对于每个 \(i\),加的都是一个前缀,那我们只需要求出最大的 \(j\) 满足 \(\text{mex}(j, i) = k\) 然后加上前缀即可。
前缀这个可以前缀和优化,那现在在于如何对于每个 \(i\) 求出 \(j\)。
因为我们求的是前缀,所以考虑倒着扫。
考虑对于目前的 \(r\) 我们求出了 \(j\),那么随着右端点左移,这个 \(j\) 肯定是左移的,不会右移,所以直接双指针然后用 set 维护 \(\text{mex}\) 即可。
时间复杂度 \(O(n \log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10, P = 998244353;
int n, a[N], b[N], pre[N], f[N], s[N];
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
set<int> q;
for (int i = 0; i <= n; i++) q.insert(i);
for (int i = 1; i <= n; i++) {
auto p = q.find(a[i]);
if (p != q.end()) q.erase(p);
}
int mex = *q.begin();
for (int i = 0; i <= n; i++) q.insert(i);
for (int i = n, j = n; i >= 1; i--) {
b[a[i]]++;
if (b[a[i]] == 1) q.erase(q.find(a[i]));
while (j >= i && *q.begin() == mex) {
pre[j] = i;
b[a[j]]--;
if (!b[a[j]]) q.insert(a[j]);
j--;
}
}
f[0] = s[0] = 1;
for (int i = 1; i <= n; i++) {
if (pre[i]) f[i] = s[pre[i] - 1];
s[i] = (s[i - 1] + f[i]) % P;
}
cout << f[n];
return 0;
}
T4
https://www.mxoj.net/problem/P110076?contestId=82
题意
给你 \(p\) 和 \(n\) 个操作:
- 给定 \(x\),\(w = x\)。
- 给定 \(x\),\(w = (w + x) \bmod p\)。
\(w\) 初始为 \(0\)。
你可以按任意顺序执行上面的操作,你要求出在 \(0 \sim p - 1\) 中有多少数无论怎样也得不到。
\(50\%\) 保证 \(n,p \leq 10^5\)。
\(100\%\) 保证 \(n,p\leq 10^6\)。
Solution
考虑我们可以枚举一个赋值操作,然后下边这些 \(+x\) 操作都可以选或不选。
那这就是一个 01 背包。用 bitset 优化可以做到 \(O(\frac{np}{w})\)。
然后他是 \(\bmod p\) 可以断环为链在倍长,也可以直接在 bitset 转移的时候把后 \(x\) 位或到前 \(x\) 位上。
然后能过 50 分。
后面这个不会了。
Code
还未调出的代码。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 1, N2 = 1e6 + 1;
int n, p;
bitset<N> f;
bitset<N2> g;
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int _; cin >> _;
while (_--) {
cin >> p >> n;
vector<int> a;
if (p <= 1e5) {
f.reset();
bool ok = 1;
for (int i = 1; i <= n; i++) {
int opt, x; cin >> opt >> x;
if (opt == 0) f[x] = 1, ok = 0;
else a.push_back(x);
}
if (ok) f[0] = 1;
for (auto v : a) {
f |= (f << v) | (f >> p - v);
for (int i = p; i <= N; i++) f[i] = 0;
}
int cnt = 0;
for (int i = 0; i < p; i++) if (!f[i]) cnt++;
cout << cnt << "\n";
} else {
g.reset();
bool ok = 1;
for (int i = 1; i <= n; i++) {
int opt, x; cin >> opt >> x;
if (opt == 0) g[x] = 1, ok = 0;
else a.push_back(x);
}
if (ok) g[0] = 1;
if (n > 1e5) {
cout << 0 << "\n";
continue;
}
for (auto v : a) {
g |= (g << v) | (g >> p - v);
for (int i = p; i <= N2; i++) g[i] = 0;
}
int cnt = 0;
for (int i = 0; i < p; i++) if (!g[i]) cnt++;
cout << cnt << "\n";
}
}
return 0;
}

浙公网安备 33010602011771号