2025.8.17模拟赛
T1
有个 \(n\times m\) 的矩阵,行编号为 \(0\dots n−1\),列编号为 \(0\dots m−1\),第 \(i\) 行第 \(j\) 列一开始为 \(im+j\)。
现在支持三种操作:交换两行,交换两列,或者交换某两个位置。求进行完 \(q\) 次操作后矩阵的形态。
\(1\le n,m\le 5000,1\le q\le 10^6\)
每行初始定义一个 \(X_i=i\),每列初始定义一个 \(Y_i=i\),交换行列直接交换 \(X\) 或 \(Y\) 即可。
再初始一个 \(a_{i,j}=im+j\),然后交换两点就是交换 \(a_{X_{x_1},Y_{y_1}},a_{X_{x_2},Y_{y_2}}\)。
原题是一个 seed 和压缩输出。
赛时代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5005, M = 1e6 + 5, P = 998244353;
uint64_t seed;
uint64_t next() {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
return seed;
}
string s;
int n, m, q;
int a[N], b[N], c[N][N];
int p17[N], p19[N], ans;
int signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m >> q >> seed >> s;
p17[0] = p19[0] = 1;
for (int i = 0; i < n; ++i)
a[i] = i;
for (int i = 0; i < m; ++i)
b[i] = i;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
c[i][j] = i * m + j;
for (int i = 1; i < N; ++i)
p17[i] = 17ll * p17[i - 1] % P, p19[i] = 19ll * p19[i - 1] % P;
for (int i = 0, r1, c1, r2, c2; i < q; ++i) {
if (s[i] == 'r')
r1 = next() % n, r2 = next() % n, swap(a[r1], a[r2]);
if (s[i] == 'c')
c1 = next() % m, c2 = next() % m, swap(b[c1], b[c2]);
if (s[i] == 'f') {
r1 = next() % n, c1 = next() % m;
r2 = next() % n, c2 = next() % m;
swap(c[a[r1]][b[c1]], c[a[r2]][b[c2]]);
}
}
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
ans = (1ll * p17[i] * p19[j] % P * c[a[i]][b[j]] % P + ans) % P;
cout << ans << '\n';
return 0;
}
T2
有 \(n\) 个砝码,根据材质不同质量只有 1g,2g,3g 三种。
现在砝码上的质量标签都遗失了,由于只有材质不同,从外表难以分辨。但所幸还有一个天平,可以用这个天平秤量砝码之间的重量关系。
某些砝码之间的重量关系已经称出来了,但其它的还不知道。
现在已经选了砝码 \(a,b\) 两个放在了天平左边,想再选另外两个放在右边,求有多少种选法使得由已知信息能确定左边更重/相等/右边更重。
\(1\le n\le 50\)。
只有 \(50\) 岂不是乱搞?
直接枚举右边的两颗,以及 \(4\) 颗砝码的质量。
我们对于所有相等的关系直接将其缩成一个点,对于所有 \(u<v\) 从 \(u\) 向 \(v\) 连一条有向边。
然后将只有出度的点设为 \(1\),只有入度的设为 \(3\),其余为 \(2\),这样一定不劣。
然后检查一下是否合法即可。
赛时代码
#include <bits/stdc++.h>
using namespace std;
const int N = 53;
int n, fa[N], id[N];
int in[N], out[N], I, J;
int A[N], B[N];
int ans1, ans2, ans3;
char s[N][N];
int find(int x) {
if (fa[x] == x)
return fa[x];
return fa[x] = find(fa[x]);
}
void merge(int x, int y) {
if ((x = find(x)) != (y = find(y)))
fa[x] = y;
}
void add(int u, int v) { ++out[u], ++in[v]; }
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> s[i] + 1, fa[i] = i;
cin >> I >> J;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (s[i][j] == '=')
merge(i, j);
for (int i = 1; i <= n; ++i)
id[i] = find(i);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (s[i][j] == '-')
add(id[i], id[j]);
for (int i = 1; i <= n; ++i) {
if (!in[id[i]])
A[id[i]] = 1;
else if (in[id[i]] && out[id[i]])
A[id[i]] = 2;
else
A[id[i]] = 3;
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
int s1 = 0, s2 = 0, s3 = 0;
if (I == i || I == j || J == i || J == j || i == j)
continue;
for (int a = 1; a <= 3; ++a)
for (int b = 1; b <= 3; ++b) {
if (id[I] == id[J] && a != b)
continue;
for (int c = 1; c <= 3; ++c) {
if (id[I] == id[i] && a != c)
continue;
if (id[J] == id[i] && b != c)
continue;
for (int d = 1; d <= 3; ++d) {
if (id[I] == id[j] && a != d)
continue;
if (id[J] == id[j] && b != d)
continue;
if (id[i] == id[j] && c != d)
continue;
for (int l = 1; l <= n; ++l)
B[l] = A[l];
B[id[I]] = a, B[id[J]] = b;
B[id[i]] = c, B[id[j]] = d;
bool fl = 1;
for (int k = 1; k <= n; ++k)
for (int l = 1; l <= n; ++l) {
if (s[k][l] == '-' && B[id[k]] >= B[id[l]])
fl = 0;
}
if (fl) {
if (a + b > c + d)
++s1;
if (a + b == c + d)
++s2;
if (a + b < c + d)
++s3;
}
}
}
}
if ((s1 && s2) || (s1 && s3) || (s2 && s3))
continue;
ans1 += (s1 > 0), ans2 += (s2 > 0), ans3 += (s3 > 0);
}
cout << ans1 / 2 << '\n' << ans2 / 2 << '\n' << ans3 / 2 << '\n';
return 0;
}
T3
一条路从 \((1,0)\) 到 \((n,0)\),店铺排列在 \((i,1)\) 和 \((i,-1)\) 的位置(\(1\le i\le n\))。\((i,1)\) 的位置有 \(a_i\) 的顾客,\((i,-1)\) 有 \(b_i\) 的顾客。
可以选择一些店铺贴上广告,一家店 \((x,y)\) 里的顾客能看到广告的条件为 \((x,y)\) 没贴广告,且存在 \(|x'-x|\le d\) 使得 \((x',-y)\) 贴了广告。求能从店里看到广告的顾客数的最大值。
\(1\le n\le 1500\)
如果一个店的人看不到广告,那么将其贴上广告肯定是不劣的。
所以在 \((x,y)\) 贴广告相当于花费 \(a_i\) 或 \(b_i\) 的代价,覆盖 \((x,y)\) 和 \((x+z,-y)(-d\le z\le d)\),求最小覆盖代价。
然后 \(dp_{i,j}\) 表示上一列覆盖到 \(i\),\(i+1\) 未覆盖,下一列覆盖到 \(j\),\(j+1\) 未覆盖。当然 \(i+2\),\(j+2\) 之后的有可能被盖,不将其计入不会影响最优性。
再枚举选哪个,考虑最远伸展到哪,就可以实现 \(n^3\)。
赛时实现较为野蛮。
80pts代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1505;
int n, d, a[N], b[N];
int sum, L, M, R;
int dp[N][N];
void Min(int &x, int y) { x = min(x, y); }
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
memset(dp, 0x3f, sizeof(dp));
cin >> n >> d;
for (int i = 1; i <= n; ++i)
cin >> a[i], sum += a[i];
for (int i = 1; i <= n; ++i)
cin >> b[i], sum += b[i];
dp[0][0] = 0;
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= n; ++j) {
L = max(j - d + 1, 1), R = min(j + d + 1, n), M = min(R, n - d);
for (int k = L; k <= M; ++k)
Min(dp[i][k + d], dp[i][j] + a[k]);
for (int k = M + 1; k <= R; ++k)
Min(dp[i][n], dp[i][j] + a[k]);
if (i < n && i <= j + d)
Min(dp[i + 1][min(n, max(j, i + d + 1))], dp[i][j] + a[i + 1]);
L = max(i - d + 1, 1), R = min(i + d + 1, n), M = min(R, n - d);
for (int k = L; k <= M; ++k)
Min(dp[k + d][j], dp[i][j] + b[k]);
for (int k = M + 1; k <= R; ++k)
Min(dp[n][j], dp[i][j] + b[k]);
if (j < n && j <= i + d)
Min(dp[min(n, max(i, j + d + 1))][j + 1], dp[i][j] + b[j + 1]);
}
}
cout << sum - dp[n][n] << '\n';
return 0;
}
然后直接上个什么线段树区间取 \(\min\) 什么的暴力维护,力大砖飞
100pts代码
#include <bits/stdc++.h>
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)
#define mid (l + r >> 1)
using namespace std;
const int N = 1505;
int n, d, a[N], b[N];
int sum, L, M, R;
int dp[N][N];
void Min(int &x, int y) { x = min(x, y); }
struct SEG {
int s[N << 2];
void build(int p, int l, int r) {
s[p] = 1e9;
if (l == r)
return;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
}
void upd(int p, int l, int r, int L, int R, int v) {
if (L <= l && r <= R)
return Min(s[p], v);
if (L <= mid)
upd(ls(p), l, mid, L, R, v);
if (mid < R)
upd(rs(p), mid + 1, r, L, R, v);
}
int ask(int p, int l, int r, int x) {
if (l == r)
return s[p];
if (x <= mid)
return min(s[p], ask(ls(p), l, mid, x));
else
return min(s[p], ask(rs(p), mid + 1, r, x));
}
} x[N], y[N];
struct BIT {
int s[N << 2];
void build(int p, int l, int r) {
s[p] = 1e9;
if (l == r)
return;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
}
void upd(int p, int l, int r, int x, int y) {
if (l == r)
return s[p] = y, void();
if (x <= mid)
upd(ls(p), l, mid, x, y);
else
upd(rs(p), mid + 1, r, x, y);
s[p] = min(s[ls(p)], s[rs(p)]);
}
int ask(int p, int l, int r, int L, int R) {
if (L > R)
return 1e9;
if (L <= l && r <= R)
return s[p];
int res = 1e9;
if (L <= mid)
Min(res, ask(ls(p), l, mid, L, R));
if (mid < R)
Min(res, ask(rs(p), mid + 1, r, L, R));
return res;
}
} X, Y;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
memset(dp, 0x3f, sizeof(dp));
cin >> n >> d;
X.build(1, 1, n), Y.build(1, 1, n);
for (int i = 1; i <= n; ++i)
cin >> a[i], sum += a[i], X.upd(1, 1, n, i, a[i]);
for (int i = 1; i <= n; ++i)
cin >> b[i], sum += b[i], Y.upd(1, 1, n, i, b[i]);
dp[0][0] = 0;
for (int i = 0; i <= n; ++i)
x[i].build(1, 0, n), y[i].build(1, 0, n);
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= n; ++j) {
if (j > d)
Min(dp[i][j], x[i].ask(1, 0, n, j) + a[j - d]);
if (i > d)
Min(dp[i][j], y[j].ask(1, 0, n, i) + b[i - d]);
L = max(j - d + 1, 1), R = min(j + d + 1, n), M = min(R, n - d);
x[i].upd(1, 0, n, L + d, M + d, dp[i][j]);
Min(dp[i][n], dp[i][j] + X.ask(1, 1, n, M + 1, R));
if (i < n && i <= j + d)
Min(dp[i + 1][min(n, max(j, i + d + 1))], dp[i][j] + a[i + 1]);
L = max(i - d + 1, 1), R = min(i + d + 1, n), M = min(R, n - d);
y[j].upd(1, 0, n, L + d, M + d, dp[i][j]);
Min(dp[n][j], dp[i][j] + Y.ask(1, 1, n, M + 1, R));
if (j < n && j <= i + d)
Min(dp[min(n, max(i, j + d + 1))][j + 1], dp[i][j] + b[j + 1]);
}
}
cout << sum - dp[n][n] << '\n';
return 0;
}
T4
??
纯属大模拟加神秘优化,赛时赛后都没管。

浙公网安备 33010602011771号