2025 暑期 mx 集训 7.31
T1
https://www.mxoj.net/problem/P110080?contestId=87
题意
有 \(n\) 个数 \(a_i\)。
你要选出两个集合,使得每个集合里的极差 \(\leq k\)。
\(n\leq 5\times 10^5\)。
Solution
简单题,先排序,然后扫一遍二分求贡献,然后前缀后缀取个 \(\max\) 枚举中间点。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10, inf = 0x3f3f3f3f;
int n, k, a[N], pre[N], suf[N];
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
sort (a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
int l = 1, r = i - 1, p = i;
while (l <= r) {
int mid = (l + r) / 2;
if (a[mid] >= a[i] - k) r = (p = mid) - 1;
else l = mid + 1;
}
pre[i] = max(pre[i - 1], i - p + 1);
}
for (int i = 1; i <= n; i++) {
int l = i + 1, r = n, p = i;
while (l <= r) {
int mid = (l + r) / 2;
if (a[mid] <= a[i] + k) l = (p = mid) + 1;
else r = mid - 1;
}
suf[i] = max(suf[i + 1], p - i + 1);
}
int ans = 0;
for (int i = 1; i < n; i++) ans = max(ans, pre[i] + suf[i + 1]);
cout << ans;
return 0;
}
T2
https://www.mxoj.net/problem/P110081?contestId=87
题意
给你一个网格,左右移动耗费 \(1\) 的时间,上下移动耗费 \(k\) 的时间。有些格子无法通行。你要保证从 \((sx, sy) \to (tx, ty)\) 的最短路恰好耗费 \(s\) 的时间。
其中 \(s,k\) 可以是小数。你要求出最小的满足要求的 \(k\)。
Solution
这个题因为要求的是最短路,当时我就列了一个式子:\(d + kx = s\),然后我肯定尽可能让 \(x\) 大,那 \(k\) 就小了。
但是在这种情况下,一定有一条使用 \(k\) 更少的路径长度肯定比 \(s\) 小。这才是最短路。
然后注意到 \(k\) 有单调性,所以直接二分答案跑最短路看长度和 \(s\) 的大小关系就行。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 10, inf = 0x3f3f3f3f;
int n, m, a[N][N];
int sx, sy, tx, ty;
double d[N][N];
bool vis[N][N];
int fx[] = { 0, 0, 1, -1 };
int fy[] = { 1, -1, 0, 0 };
double res;
struct node {
int x, y;
double d;
bool operator < (const node &b) const {
return d > b.d;
}
};
void dijkstra(double k)
{
priority_queue<node> q;
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) d[i][j] = 1e9, vis[i][j] = 0;
d[sx][sy] = 0; q.push({sx, sy, 0});
while (!q.empty()) {
int x = q.top().x, y = q.top().y; q.pop();
if (vis[x][y]) continue;
vis[x][y] = 1;
for (int i = 0; i < 4; i++) {
int dx = x + fx[i], dy = y + fy[i];
if (dx < 1 || dx > n || dy < 1 || dy > m || a[dx][dy]) continue;
double w = (i < 2 ? 1.0 : k);
if (!(d[dx][dy] <= d[x][y] + w + (1e-4))) {
d[dx][dy] = d[x][y] + w;
q.push({dx, dy, d[dx][dy]});
}
}
}
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n >> m >> sx >> sy >> tx >> ty;
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> a[i][j];
cin >> res;
double l = 0, r = res;
while (r - l > 1e-4) {
double mid = (l + r) / 2;
dijkstra(mid);
if (res <= d[tx][ty] + (1e-4)) r = mid;
else l = mid;
}
cout << fixed << setprecision(3) << l + (5e-5);
return 0;
}
T3
https://www.mxoj.net/problem/P110082?contestId=87
题意
给你一个长度为 \(n\) 的字符串。
你要把这个字符串分成 \(k\) 个长度为 \(x\) 的子串,以及一个长度为 \(m\) 的串。
满足 \(kx + m = n\) 且 \(m < x\),\(m\) 可以为 \(0\)。
然后你就把这些长度为 \(x\) 的串任意重排然后连接起来。求对于每个 \(x\) 能组合出多少种不同的字符串,把所有 \(x\) 的答案加起来输出。对 \(998244353\) 取模。
\(n \leq 3\times 10^5\)。
Solution
首先考虑 \(x\) 的取值是 \([1, n]\) 的,然后就可以枚举 \(x\),然后如果有 \(m\) 的话,可以枚举长为 \(m\) 的这个串的开头,然后算贡献。
一开始卡在如何快速算贡献上了,但是很快想到多重集排列,于是 \(n^3\) 就拿到了。
接着考虑实际上它每次是跳 \(x\) 的,这个复杂度总共是 \(O(n \ln n)\) 的,于是套上 hash 就有了一个 \(O(n^2 \ln n)\) 的做法。有 50 分。
但是我写出来答案大了,我就猜是算重了。如果把所有长为 \(x\) 的子串拿出来看作一个集合,那么如果有两个集合重了,则就去掉其中一个的贡献。这个可以用随机赋权的哈希来解决。
题面中说了,所有长为 \(x\) 的子串得连续,所以就不存在 \(m\) 把一个 \(x\) 分搁开的情况。
所以枚举 \(m\) 的位置也是 \(\frac{n}{x}\) 的。
然后好像是维护前后缀,目前还没搞明白。
T4
https://www.mxoj.net/problem/P110083?contestId=87
题意
给你一颗树,求有多少路径 \((u,v)\) 满足路径上的最大值和最小值均出现在端点处。
\(n\leq 5\times 10^5\)。
Solution
暴力点分治,\(O(n\log^2 n)\) 卡过。我不会。
考虑类似 Kruskal 重构树。对最小生成树跑一个重构树,对最大生成树跑一个重构树。
那么现在符合条件的 \((u, v)\) 相当于在一棵树上,\(u\) 是 \(v\) 的祖先,在另一颗树上,\(v\) 是 \(u\) 的祖先。
那这样就好维护了。先跑一个 dfs 序。
我们遍历第一颗树,把从根到当前这个点的这条链上的点全在树状数组里 \(+1\)。
这个只要在进来的时候加上,出去的时候减掉就可完成这个操作。
然后查询当前点的子树和,就是关于这个点的答案。
由于我们算上了每个点和他自己,题目中不让算,所以最后 \(-n\) 就行。
时间复杂度 \(O(n\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 5e5 + 10, inf = 0x3f3f3f3f;
int n, p[N], L[N], R[N], cnt;
ll c[N], ans;
vector<int> e[N], g[N], G[N];
int fifa(int x) { return p[x] == x ? x : p[x] = fifa(p[x]); }
#define lb(o) ((o) & (-o))
void add(int x, int v) { for (; x <= n; x += lb(x)) c[x] += v; }
ll qry(int x) { ll res = 0; for (; x; x -= lb(x)) res += c[x]; return res; }
void dfs(int u)
{
L[u] = ++cnt;
for (auto v : g[u]) dfs(v);
R[u] = cnt;
}
void dfs1(int u)
{
add(L[u], 1);
ans += qry(R[u]) - qry(L[u] - 1);
for (auto v : G[u]) dfs1(v);
add(L[u], -1);
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i < n; i++) {
int u, v; cin >> u >> v;
e[u].push_back(v), e[v].push_back(u);
}
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 1; i <= n; i++) {
for (auto v : e[i]) {
if (v >= i) continue;
int x = fifa(i), y = fifa(v);
p[y] = x;
g[x].push_back(y);
}
}
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = n; i >= 1; i--) {
for (auto v : e[i]) {
if (v <= i) continue;
int x = fifa(i), y = fifa(v);
p[y] = x;
G[x].push_back(y);
}
}
dfs(n);
dfs1(1);
cout << ans - n;
return 0;
}

浙公网安备 33010602011771号