2024年3月ZZUACM 招新赛
题号 | 题目 |
---|---|
A | 区间次大值 |
B | 上课签到 |
C | 魔法森林(一) |
D | 魔法森林(二) |
E | LOP |
F | 跳格子 |
G | 猜数字 |
H | 抽卡记录 |
I | 安达的二维矩阵 |
J | 安达的数字手术 |
K | 跳楼梯 |
L | 前缀和 |
A 区间次大值—循环/签到题
题目描述
给定一个 n n n的全排列 a i a_i ai,下标为 1 − n 1-n 1−n,请你输出所有 l < r l<r l<r的区间 [ l , r ] [l,r] [l,r]中次大数之和。
n n n的全排列指一个长为 n n n的数组, 1 , 2... N 1,2...N 1,2...N每个数字出现且只出现一次。
输入格式
第一行一个整数表示 n n n
第二行 n n n个整数表示 n n n的全排列
输出格式
输出一行一个正整数表示答案
输入输出样例
样例输入 #1
4
4 1 3 2
样例输出 #1
12
样例解释 #1
区间 [ 1 , 2 ] [1,2] [1,2]的次大值为 1 1 1,区间 [ 1 , 3 ] [1,3] [1,3]的次大值为 3 3 3,区间 [ 1 , 4 ] [1,4] [1,4]的次大值为 3 3 3,区间 [ 2 , 3 ] [2,3] [2,3]的次大值为 1 1 1,区间 [ 2 , 4 ] [2,4] [2,4]的次大值为 2 2 2,区间 [ 3 , 4 ] [3,4] [3,4]的次大值为 2 2 2。因此答案为 1 + 3 + 3 + 1 + 2 + 2 = 12 1+3+3+1+2+2=12 1+3+3+1+2+2=12
数据范围与约定
数据保证 n ≤ 1 0 3 n\leq 10^3 n≤103, 1 ≤ a i ≤ n 1\leq a_i \leq n 1≤ai≤n。如果 i ≠ j i\neq j i=j,则 a i ≠ a j a_i \neq a_j ai=aj。
题解
先看数据范围可二层循环,可直接暴力,第一层循环代表左端点,第二层右端点,维护一个最大值和次大值即可
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n; cin >> n;
vector<int> a(n + 1, 0);
for(int i = 0; i < n; i ++) cin >> a[i];
int res = 0;
for(int i = 0; i < n - 1; i ++) {
int max1 = max(a[i], a[i + 1]);
int max2 = min(a[i], a[i + 1]);
res += max2;
for(int j = i + 2; j < n; j ++) {
if(a[j] > max1) {
max2 = max1;
max1 = a[j];
} else if(a[j] > max2) {
max2 = a[j];
}
res += max2;
}
}
cout << res << endl;
}
B 上课签到—二分+最短路
题目描述
某一天飞云从宿舍起床,但他上课快迟到了,他想要尽可能快的到达教室。他要在上课之前到达教室签到,换句话说,如果还有 h h h分钟上课,他必须要在小于等于 h h h分钟内到达教室。现在将宿舍,十字路口,教室抽象成一张无向图,含有 n n n个点和 m m m条边。由于路况的不同,每到达一个点都要消耗 a i a_i ai体力值(起始位置和终止位置也算),每经过一条边需要 w w w分钟。而对于飞云来说,由于体力可以恢复,所以他只需要知道路径上的最大体力消耗。现在飞云向聪明的你求助,在不迟到的情况下,所选路径中最大体力消耗的最小值是多少。
输入格式
第一行读入五个数 n , m , s t , e d , h n,m,st,ed, h n,m,st,ed,h(分别无向图的点数,边数,起始位置,终止位置,距离上课所剩的时间(单位:分钟))
接下来n行分别读入 n n n个数 a i a_i ai(每个点消耗的体力值)
接下来m行读入 x , y , w x,y,w x,y,w(分别代表无向边的两点和路上所花费的时间)
输出格式
输出一行代表最大消耗体力的最小值,若会迟到,则输出 − 1 -1 −1
输入输出样例
样例输入 #1
4 4 1 4 8
8
5
6
10
1 3 4
2 4 1
2 1 2
3 4 9
样例输出 #1
10
样例解释
只能选择路径1->2->4
,花费 3 3 3分钟,路径上最大体力消耗是到达 4 4 4的时候,花费 10 10 10.
数据范围与约定
$1 \le n \le 10^4 $, 1 ≤ m ≤ 2 ∗ 1 0 4 1 \le m\le 2*10^4 1≤m≤2∗104, 1 ≤ a i , z , h ≤ 1 0 7 1 \le a_i,z, h \le 10^7 1≤ai,z,h≤107, 1 ≤ x , y ≤ n 1 \le x,y \le n 1≤x,y≤n
题解
首先,对于最大…的最小,都可以考虑二分思路。其次,对于双权值问题,如果可以开二维,是可以跑二维的,但是明显这题开不了二维(会爆栈和TLE),并且有单调性质,所以考虑二分。
单调性质:设最大体力消耗是x,如果x满足条件(即可以找到一条路径,路径的权值和小于等于h并且点权值都小于等于x),那么大于x的也满足条件。所以考虑二分。
然后判断是不是满足跑一下dijkstra最短路即可。
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
#define PII pair<int, int>
#define int long long
const int N = 1e4 + 10;
int a[N];
struct edge {
int v, w;
};
vector<edge> e[N];
int n, m, st, ed, h;
int dist[N];
int vis[N];
bool check(int x) {
// Dijkstra最短路
for (int i = 1; i <= n; i++) {
dist[i] = 1e18;
vis[i] = 0;
}
priority_queue<PII, vector<PII>, greater<PII>> q;
q.push({
0, st});
dist[st] = 0;
if (a[st] > x) return false;
while (q.size()) {
auto now = q.top();
q.pop();
int u = now.second;
if (vis[u]) continue;
vis[u] = 1;
for (auto t: e[u]) {
int v = t.v, w = t.w;
if (dist[v] > dist[u] + w && a[v] <= x) {
dist[v] = dist[u] + w;
q.push({
dist[v], v});
}
}
}
return dist[ed] <= h;
}
signed main() {
cin >> n >> m >> st >> ed >> h;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
int u, v, w; cin >> u >> v >> w;
e[u].push_back({
v, w});
e[v].push_back({
u, w});
}
int l = 1, r = 1e7 + 10;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (l == 1e7 + 10) cout << -1 << '\n';
else cout << l << '\n';
}
C 魔法森林(一)—模拟
题目描述
小G来到了向往已久的魔法森林,并且想要探索其中的宝藏。
具体的,魔法森林可以看作是一个 n × m n\times m n×m的矩阵,其中矩阵的每个位置都可以用一对坐标表示,例如 ( i , j ) (i,j) (i,j)第 i i i行第 j j j列的位置。魔法森林中每个位置的具体信息可以用一个字符来表示:
若当前位置字符是’.',则表示当前位置为空地,小G可以自由行走。
若当前位置字符是’#',则表示当前位置为障碍,小G不可以通过。
另外作为魔法森林,魔法传送阵是其特色,具体的来说若字符是大写的英文字母,则