2025.8.30 模拟赛

T1

由于绝对值即使取负数对求最大值的答案也没有影响。

\[|x_i-x_j|+|y_i-y_j|= \max(x_i-x_j,x_j-x_i)+\max(y_i-y_j,y_j-y_i) \]

\[=max(x_i-x_j+y_i-y_j,x_j-x_i+y_i-y_j,x_i-x_j+y_j-y_i,x_j-x_i+y_j-y_i) \]

对于更高维的也就是 \(2^k\) 次方枚举符号的问题,复杂度 \(O(n2^k)\)

赛时代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, k;
int x[N][4];
int p[N], q[N], ans;
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n >> k;
    for (int i = 1; i <= n; ++i)
        for (int j = 0; j < k; ++j)
            cin >> x[i][j];
    for (int s = 0; s < (1 << k); ++s) {
        p[0] = q[n + 1] = 1e9;
        for (int i = 1; i <= n; ++i) {
            p[i] = q[i] = 0;
            for (int j = 0; j < k; ++j) {
                p[i] += x[i][j] * ((s >> j & 1) ? 1 : -1);
                q[i] += x[i][j] * ((s >> j & 1) ? 1 : -1);
            }
            p[i] = min(p[i], p[i - 1]);
        }
        for (int i = n; i >= 1; --i) {
            q[i] = min(q[i], q[i + 1]);
            int res = 0;
            for (int j = 0; j < k; ++j)
                res += x[i][j] * ((s >> j & 1) ? 1 : -1);
            ans = max(ans, res - min(p[i - 1], q[i + 1]));
        }
    }
    cout << ans << '\n';
    return 0;
}

T2

给出一个字符串的 \(kmp\) 数组,求它的 \(Z\) 函数数组,保证有解。\(1\le n\le 10^6\)

赛题有一点区别就是 \(Z\) 函数 \(z_1=0\)\(z_1=n\) 的区别。

然后一段 \(kmp_i\) 告诉我们某两端是相等的,用并查集合并一下,求 \(z\) 的时候判断相等就是判断是否在同一集合。

然后根据 kmp 的性质,每次我们只用合并 \(i\)\(kmp_i\) 这两个位置就好了。

赛时代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, nxt[N], fa[N], z[N];
void Max(int &x, int y) { x = max(x, y); }
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int x, int y) {
    if ((x = find(x)) != (y = find(y)))
        fa[x] = y;
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        fa[i] = i;
        cin >> nxt[i];
        if (nxt[i])
            merge(i, nxt[i]);
    }
    z[1] = n;
    for (int i = 2, l = 0, r = 0; i <= n; ++i) {
        if (i <= r)
            z[i] = min(z[i - l + 1], r - i + 1);
        while (i + z[i] <= n && find(i + z[i]) == find(z[i] + 1))
            ++z[i];
        if (i + z[i] - 1 > r)
            l = i, r = i + z[i] - 1;
    }
    for (int i = 1; i <= n; ++i)
        cout << z[i] << ' ';
    return 0;
}

T3

  x1
 x23
x456
....

给一个 \(n\) 行的三角 \(a\) 形状如上,第 \(i\) 行有 \(i\) 个数,有 \(m\) 个限制形如 \(a_{x,y}\le z\),以及要求每一个左上角为 x 的矩形的左下,右上,右下三个数 \(b_1,b_2,b_3\) 有任意 \(b_i\le b_j+b_k\)。例如编号 \(6\) 这个位置有 \(\le 1+5,\le 3+4\)\(q\) 次询问 \(a_{x_1,y_1}+a_{x_2,y_2}\) 的最大可能值。

这玩意形似差分约束,长得又像 floyd 的过程。将这个三角形在左上方复制一遍就可以解决互相关系,然后做一遍类似 floyd 的东西。诈骗的地方在于存在方案可以最大化全局,\(q\) 次询问是假的。

赛时代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n, m, q, a[N][N];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n >> m >> q, memset(a, 0x3f, sizeof(a));
    ++n;
    for (int i = 1, x, y, z; i <= m; ++i) {
        cin >> x >> y >> z;
        y = n - x + y, ++x;
        a[x][y] = a[n - y + 1][n - x + 1] = z;
    }
    for (int k = 1; k <= n; ++k) {
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                a[i][j] = min(a[i][j], a[i][k] + a[n - k + 1][j]);
            }
        }
    }
    for (int x, y, i, j; q; --q)
        cin >> x >> y >> i >> j, cout << a[x + 1][n - x + y] + a[i + 1][n - i + j] << '\n';

    return 0;
}
posted @ 2025-08-30 11:05  zzy0618  阅读(9)  评论(0)    收藏  举报