2021NUAA暑期模拟赛部分题解
比赛链接:2021 nuaa 暑期模拟赛
比赛码:1234554321
D - 然叔的玩具熊
答案是单调的,考虑每个数能成为答案的最大区间长度,即这个数是区间最小值,所以找每个数前后第一个小于它的即可得到最长长度,短的长度可以通过后缀最大传递下来。
单调栈可以\(O(n)\)。
#include <cstdio>
#include <iostream>
#include <stack>
using namespace std;
int n, h[200010];
int len[200010]; // len[i]表示大小为i的分组中力量的最大值
struct node { // num:数值 loc:能被作为答案的最大区间的最左端位置
int num, loc;
};
stack<node> st;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &h[i]);
}
h[++n] = 0; // 在序列末尾加上0使其他数都出栈
for (int i = 1; i <= n; ++i) {
if (st.empty()) {
st.push(node{h[i], i});
} else if (!st.empty() && h[i] >= st.top().num) {
st.push(node{h[i], i});
} else {
int nowloc = i;
while (!st.empty() && st.top().num > h[i]) {
int loc = st.top().loc, x = st.top().num;
st.pop();
nowloc = loc; // 区间变大,最左端更新
if (len[i - loc] < x) { // 更新答案
len[i - loc] = x;
}
}
st.push(node{h[i], nowloc});
}
}
for (int i = n - 1; i >= 1; --i) { // 从后往前更改答案使其单调递减
if (len[i] < len[i + 1]) {
len[i] = len[i + 1];
}
}
for (int i = 1; i <= n - 1; ++i) {
printf("%d ", len[i]);
}
return 0;
}
H - Good Array
签到题,分两种情况直接输出即可。
#include <iostream>
using namespace std;
int t, n, s;
int main() {
s = 0;
cin >> t;
while (t--) {
s = 0;
int x;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> x;
s += x;
}
if (s >= n) {
cout << s - n << endl;
} else {
cout << 1 << endl;
}
}
return 0;
}
I - 恶灵传说
首先通过广度优先搜索找出所有存在危险的城市,通过堆优化时间。
再通过最短路找到最小花费,安全城市花费为P,危险城市花费为Q,被占领城市花费为无穷大即不可走。
#include <iostream>
#include <queue>
using namespace std;
int n, m, k, s, p, q, x;
int vis[100010]; // 0 安全 1 危险 2 被占领 3 被占领
int cnt, head[100010];
long long dis[100010];
struct Edge {
int len, v, next;
} edge[400010];
struct dj_node {
int id;
long long dis;
bool operator<(const dj_node &obj) const { return dis > obj.dis; }
};
struct bfs_node {
int id, dis;
bool operator<(const bfs_node &obj) const { return dis < obj.dis; }
};
priority_queue<bfs_node> bfs_pq;
void addEdge(int u, int v, int w) {
edge[++cnt].v = v;
edge[cnt].len = w;
edge[cnt].next = head[u];
head[u] = cnt;
// 无向图
edge[++cnt].v = u;
edge[cnt].len = w;
edge[cnt].next = head[v];
head[v] = cnt;
}
void find_danger() { // 堆优化广搜
while (!bfs_pq.empty()) {
int id = bfs_pq.top().id, dis = bfs_pq.top().dis;
bfs_pq.pop();
if (vis[id] != 1 && vis[id] != 3) {
for (int i = head[id]; i; i = edge[i].next) {
if (edge[i].len <= dis) {
bfs_pq.push(bfs_node{edge[i].v, dis - edge[i].len});
}
}
if (vis[id] == 0) {
vis[id] = 1;
} else if (vis[id] == 2) {
vis[id] = 3;
}
}
}
}
void dijkstra() { // 最短路
priority_queue<dj_node> dj_pq;
for (int i = 1; i <= n; ++i) {
dis[i] = 0x3f3f3f3f;
}
dis[1] = 0;
dj_pq.push(dj_node{1, 0});
while (!dj_pq.empty()) {
int id = dj_pq.top().id;
long long Dis = dj_pq.top().dis;
dj_pq.pop();
if (Dis > dis[id]) continue;
for (int i = head[id]; i; i = edge[i].next) {
if (vis[edge[i].v] == 3) {
continue;
} else if (vis[edge[i].v] == 1) {
if (dis[edge[i].v] > Dis + q) {
dis[edge[i].v] = Dis + q;
dj_pq.push(dj_node{edge[i].v, dis[edge[i].v]});
}
} else if (vis[edge[i].v] == 0) {
if (dis[edge[i].v] > Dis + p) {
dis[edge[i].v] = Dis + p;
dj_pq.push(dj_node{edge[i].v, dis[edge[i].v]});
}
}
}
}
}
int main() {
cin >> n >> m >> k >> s >> p >> q;
for (int i = 1; i <= k; ++i) {
cin >> x;
vis[x] = 2;
bfs_pq.push(bfs_node{x, s});
}
int u, v, w;
for (int i = 1; i <= m; ++i) {
cin >> u >> v >> w;
addEdge(u, v, w);
}
find_danger();
dijkstra();
if (vis[n] == 3) {
cout << -1 << endl;
} else if (vis[n] == 1) {
cout << dis[n] - q << endl;
} else {
cout << dis[n] - p << endl;
}
return 0;
}
K - 数字矩阵
标记多少行多少列有\(0\)即可,如果\(i\)行\(j\)列有\(0\),答案即为\(i*m+j*n-i*j\)。
#include <iostream>
using namespace std;
int x[2010], y[2010], ii, jj, n, m;
int main() {
cin >> n >> m;
int num;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> num;
if (!num) {
x[i] = 1;
y[j] = 1;
}
}
}
for (int i = 1; i <= n; ++i) {
ii += x[i];
}
for (int j = 1; j <= m; ++j) {
jj += y[j];
}
cout << ii * m + jj * n - ii * jj << endl;
return 0;
}