NOIP 2023 比赛报告
第一题
比赛情况
\(100\) 分,耗时 \(1\) 小时。
题解
对于 \(1\le i\le n\) ,比较 \(w_i\) 字典序最小的字符 \(a_i\) 与每个 \(w_j(i\ne j)\) 字典序最大的字符 \(b_j\) 。如果有 \(b_j\le a_i\) ,则 \(w_i\) 不能成为字典序最小的单词,反之可以。
代码
点击查看代码
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 3005, M = 3005;
int n, m;
char s[N][M], mx[N], mn[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
memset(mx, 0, sizeof(mx));
memset(mn, 0x7f, sizeof(mn));
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> s[i];
for (int j = 0; j < m; j++) {
mx[i] = max(mx[i], s[i][j]);
mn[i] = min(mn[i], s[i][j]);
}
}
for (int i = 0; i < n; i++) {
bool flag = true;
for (int j = 0; j < n; j++) {
if (i == j)
continue;
if (mn[i] >= mx[j]) {
flag = false;
break;
}
}
if (flag)
cout << "1";
else
cout << "0";
}
return 0;
}
第二题
比赛情况
\(100\) 分,耗时 \(2\) 小时。
题解
对于每个变量,记录为与其他变量初值的关系或者为某个定值。
建立一个无向图,当变量 \(i\) 为变量 \(j\) 的初值时,连接 \(i,j\) ,边权为 \(0\) ;当变量 \(i\) 为变量 \(j\) 的初值的非时,连接 \(i,j\) ,边权为 \(1\) 。
对图进行 DFS,如果一个环总共的边权和为奇数或有一个点对应变量为定值 \(U\) ,则整个连通分量中的点对应的变量的初值应为 \(U\) 。
代码
点击查看代码
#include <iostream>
using namespace std;
const int N = 1e5 + 5, M = 1e5 + 5;
int C, T;
int n, m, x[N], y[N];
struct Edge {
int v, w, next;
} edge[M << 1];
int head[N], cnt = 0;
void addedge(int u, int v, int w) {
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt++;
}
int fa[N];
int find(int x) { return fa[x] = (x == fa[x] ? x : find(fa[x])); }
void merge(int x, int y) {
int fx = find(x), fy = find(y);
fa[fx] = fy;
}
int val[N], unk[N];
void dfs(int u, int fa) {
int g = find(u);
if (unk[g])
return;
for (int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].v, w = edge[i].w * val[u];
if (v == fa)
continue;
if (val[v]) {
if (val[v] != w) {
unk[g] = 1;
return;
}
continue;
}
val[v] = w;
dfs(v, u);
if (unk[g])
return;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> C >> T;
while (T--) {
cnt = 0;
cin >> n >> m;
for (int i = 1; i <= n; i++)
x[i] = 1, y[i] = i, head[i] = -1, val[i] = unk[i] = 0, fa[i] = i;
while (m--) {
char s[5], op;
cin >> s;
op = s[0];
if (op == 'T') {
int i;
cin >> i;
x[i] = 0, y[i] = 1;
} else if (op == 'F') {
int i;
cin >> i;
x[i] = 0, y[i] = -1;
} else if (op == '+') {
int i, j;
cin >> i >> j;
x[i] = x[j], y[i] = y[j];
} else if (op == '-') {
int i, j;
cin >> i >> j;
x[i] = x[j], y[i] = -y[j];
} else {
int i;
cin >> i;
x[i] = 0, y[i] = 0;
}
}
for (int i = 1; i <= n; i++) {
if (x[i]) {
int u = i, v, w;
if (y[i] > 0)
v = y[i], w = 1;
else
v = -y[i], w = -1;
addedge(u, v, w);
addedge(v, u, w);
merge(u, v);
}
}
for (int i = 1; i <= n; i++)
if (!x[i])
unk[find(i)] = (y[i] ? -1 : 1);
for (int i = 1; i <= n; i++) {
int g = find(i);
if (x[i] && !unk[g]) {
val[i] = 1;
dfs(i, 0);
if (!unk[g])
unk[g] = -1;
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
if (unk[find(i)] == 1)
ans++;
cout << ans << endl;
}
return 0;
}
第三题
比赛情况
没做。
题解
可以看做可以从 \((x,y)\) 走到 \((x+1,y),(x,y+1),(x+1,y+1)\) ,要求 \((1,1)\) 走到 \((n,m)\) 并且对所有经过的点 \((x,y)\) 都有 \(a_x<b_y\) 或 \(a_x>b_y\) 。
分析当 \(a_x<b_y\) 的情况,另一种情况同理。
对于序列 \(a_{1\dots i}\) 与 \(b_{1\dots j}\) 来说,如果 \(a_{\min}<b_{\min}\) ,那么 \(a_{\min}\) 所在行都合法,否则不合法;如果 \(a_{\max}<b_{\max}\) ,那么 \(b_{\max}\) 所在列都合法,否则不合法。接下来再分析左上角和右下角的情况,如此递归下去。
代码
第四题
比赛情况
DP 没写出来。
题解
记 \(f_i\) 为前 \(i\) 天内,第 \(i\) 天没跑的能量值。
所以 \(f_i=\max\{f_{i-1},\max\limits_{j=i-k-1}^{i-1}[f_j-(i-j-1)d+val(j+1,i-1)]\}\) ,其中 \(val(L,R)=\sum\limits_i^{L\le l_i\le r_i\le R}v_i\) 。
可以发现我们只需要 \(f_{l_i-1}\) 或 \(f_{r_i+1}\) 的值,线段树优化即可。