传送门
A - 材料打印
原题
小Y今天去打印毕业材料。这些材料中有 a 页既可以黑白打印也可以彩印,还有 b 页必须彩印。黑白打印一页需要 x 元,彩色打印一页需要 y 元,请计算他最少需要多少钱?
输入
第一行给出一个整数 T T T( 1 ≤ T ≤ 1 0 5 1 \le T \le 10^5 1≤T≤105)表示数据组数
对于每组测试数据,给出一行共四个整数 a , b , x , y a,b,x,y a,b,x,y( 1 ≤ a , b , x , y ≤ 1 0 9 1 \le a,b,x,y \le 10^9 1≤a,b,x,y≤109)
输出
每组测试数据输出一行,一个整数,表示小Y的最小花费。
测试样例
input
3
2 2 3 4
2 2 4 3
1 14514 1 919810
output
14
12
13350122341
分析
- 有b页纸必须彩印,这b页纸的价格是固定的,只需要让a页纸以最小的价钱打印即可
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
void solve() {
int a, b, x, y;
std::cin >> a >> b >> x >> y;
i64 ans = 1LL * b * y;
if(x < y) {
ans += 1LL * a * x;
} else {
ans += 1LL * a * y;
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int t;
std::cin >> t;
while(t--) {
solve();
}
return 0;
}
B - %%%
原题
对于一个整数 n,存在一种操作:将 n 对一个不大于 n 的正整数 mod 取余(1≤mod≤n),并将结果再赋值给 n。(即:n=n%mod)
请问 n 变为 0 最多需要多少次操作?
输入
第一行给定一个整数 T T T( 1 ≤ T ≤ 1 0 5 1 \le T \le 10^5 1≤T≤105)下面为 T T T组数据,每行给定一个整数
输出
输出 T T T 行,每行一个整数
测试样例
input
3
0
3
114514
output
0
2
16
分析
- 对于任意整数 x x x,要使操作次数最多,应该让每次操作后这个数尽量大,那么就应该mod较为中间的一个数,我们可以列举几个数
- 比如,16 % 7 = 2, 16 % 9 = 7,21 % 10 = 1,21 % 11 = 10,52 % 25 = 2,52 % 27 = 25,63 % 31 = 1,63 % 32 = 31
- 综上可见,每次取 x / 2 + 1进行模运算即可
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
void solve() {
i64 n;
std::cin >> n;
if(n == 0) {
std::cout << 0 << "\n";
return;
}
int ans = 0;
while(n) {
i64 cur = n / 2 + 1;
n %= cur;
ans++;
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int t;
std::cin >> t;
while(t--) {
solve();
}
return 0;
}
C - 迷宫
原题
给定一个 n×m 的迷宫,迷宫由 “#” 与"." 两种字符组成。其中 “#” 代表障碍物,“.” 表示空地。迷宫中还有一个起点 “S” 和一个终点 “E” ,它们都可以视为空地。
由于近期迷宫发生了塌方,导致起点和终点之间可能并不连通。幸运的是,你拥有一种超能力——在迷宫中移动时(移动方向为上、下、左、右四个方向之一),可以在当前位置朝任一方向(上、下、左、右四个方向之一)释放激光。激光能够清除该方向上所有的障碍物,并且这种超能力至多只能使用一次。
现在,你需要判断是否能利用这种超能力成功从起点到达终点。
输入
第一行给定两个整数 n,m(2≤n,m≤1000) ,分别表示迷宫的行数和列数。
下面 n 行,每行 m 个字符,描述迷宫的具体布局。字符只包含 “#”、“.”、“S” 和 “E”,并且起点与终点有且仅有一个。
输出
能够到达终点输出 YES ;否则输出 NO。
测试样例
input1
4 5
.####
S####
.####
.E###
output1
YES
input2
4 5
…###
S####
#####
##.E#
output2
YES
input3
4 5
…###
S####
#####
###E#
output3
NO
分析
- 由于该超能力可以清除任一方向的所以障碍,那么这就意味着,如果可以使用超能力到达与终点连通的空地上,那么必然可以到达终点
- 对于从起点能到达的空地,我们对其bfs所有的行和列放到 s s r , s s c ssr, ssc ssr,ssc中
- 对于从终点能到达的空地,我们对其bfs所有的行和列也放到 e e r , e e c eer, eec eer,eec中
- 依次检查行和列,如果存在差值 ≤ 1 \leq 1 ≤1的即可,因为超能力会多产生一行或一列空地
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
int dx[] = {0, -1, 1, 0};
int dy[] = {1, 0, 0, -1};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n, m;
std::cin >> n >> m;
int sr, sc, er, ec;
std::vector g(n, std::vector<char>(m));
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
std::cin >> g[i][j];
if(g[i][j] == 'S') {
sr = i;
sc = j;
} else if (g[i][j] == 'E') {
er = i;
ec = j;
}
}
}
std::vector viss(n, std::vector<bool>(m, false));
std::vector vise(n, std::vector<bool>(m, false));
std::set<int> ssr, ssc, eer, eec;
auto valid = [&](int x, int y, int c) -> bool {
if(x >= 0 && x < n && y >= 0 && y < m && g[x][y] != '#') {
if(c == 1) {
if(viss[x][y]) {
return false;
} else {
return true;
}
} else {
if(vise[x][y]) {
return false;
} else {
return true;
}
}
}
return false;
};
auto bfs = [&](int x, int y, int c) -> void {
std::queue<std::pair<int, int>> q;
q.push({x, y});
while(!q.empty()) {
auto [nx, ny] = q.front();
q.pop();
if(c == 1) {
if(viss[nx][ny]) {
continue;
}
viss[nx][ny] = true;
ssr.insert(nx);
ssc.insert(ny);
} else {
if(vise[nx][ny]) {
continue;
}
vise[nx][ny] = true;
eer.insert(nx);
eec.insert(ny);
}
for(int i = 0; i < 4; i++) {
int tx = nx + dx[i];
int ty = ny + dy[i];
if(valid(tx, ty, c)) {
q.push({tx, ty});
}
}
}
};
bfs(sr, sc, 1);
bfs(er, ec, 2);
for(auto x : ssr) {
for(auto y : eer) {
if(std::abs(x - y) <= 1) {
std::cout << "YES";
return 0;
}
}
}
for(auto x : ssc) {
for(auto y : eec) {
if(std::abs(x - y) <= 1) {
std::cout << "YES";
return 0;
}
}
}
std::cout << "NO";
return 0;
}
D - 又是一年毕业季
原题
又是一年一度的毕业季,一群学生准备去拍摄毕业照。
具体来说,一共有 n 个同学共同参与拍照,第 i 个学生两次眨眼的时间间隔为 a i a_{i} ai。在拍照开始前,我们可以认为所有人在第 0 秒都眨了一次眼。
由于调整相机需要一定的时间,因此在最初的2秒内不能进行拍照(即需要保证拍照的时刻 x≥2)。请计算,为了确保每个人的眼睛都处于睁开状态,至少需要等待多少秒后才能进行拍照。
输入
第一行给出一个整数 T T T ( 1 ≤ T ≤ 5 × 1 0 4 1 \le T \le 5 \times 10^4 1≤T≤5×104)
对于每组测试数据,第一行给出一个整数 n ( 1 ≤ n ≤ 2 × 1 0 5 , ∑ n ≤ 2 ∗ 1 0 5 ) n(1 \le n \le 2 \times10^5, \sum n \le 2 * 10^5) n(1≤n≤2×105,∑n≤2∗105),表示总人数
第二行给出 n 个整数 a i ( 2 ≤ a i ≤ 1 0 9 ) a_{i}(2 \le a_{i} \le 10^9) ai(2≤ai≤109),依次表示每个人两次眨眼的间隔时间。
输出
每组测试数据输出一个整数,表示最小等待时间。
测试样例
input1
3
4
2 4 6 5
5
6 2 5 3 2333333
8
11 4 5 14 19 19 8 10
output1
3
7
2
分析
- 如果给出的n个数中没有 2 2 2,那么直接输出 2 2 2 即可,否则输出没有出现的最小的质数即可,因为对于任意合数,一定可以做质因数分解,也就是是说某个人会闭眼
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
std::vector<int> minp, primes;
void sieve(int n) {
minp.assign(n + 1, 0);
primes.clear();
for (int i = 2; i <= n; i++) {
if (minp[i] == 0) {
minp[i] = i;
primes.push_back(i);
}
for (auto p : primes) {
if (i * p > n) {
break;
}
minp[i * p] = p;
if (p == minp[i]) {
break;
}
}
}
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for(int i = 0; i < n; i++) {
std::cin >> a[i];
}
std::sort(all(a));
int ans = 0;
for(int i = 0; i < n; i++) {
if(a[i] > primes[ans]) {
break;
} else if(a[i] < primes[ans]) {
continue;
} else {
ans++;
}
}
std::cout << primes[ans] << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
sieve(1e7);
int t;
std::cin >> t;
while(t--) {
solve();
}
return 0;
}
E - 多米诺骨牌
原题
小利同学摆放了 n n n 张多米诺骨牌,第 i i i 张骨牌高为 h i h_{i} hi,摆放在位置 x i x_{i} xi,该骨牌向后倒塌时会使位于[ x i , h i + x i x_{i}, h_{i} + x_{i} xi,hi+xi]区间(包含 x i x_{i} xi, h i + x i h_{i} + x_{i} hi+xi)的骨牌全部倒塌。
小利同学摆放完骨牌就开心的出去玩耍了。结果小金同学发现了这些骨牌,他想趁小利不在,选择不超过 m 张骨牌并依次向后推倒,请算算最多会有多少张骨牌倒塌。
保证任意位置上至多存在一张骨牌。
输入
第一行给出一个整数 T T T( 1 ≤ T ≤ 1 0 5 1 \le T \le 10^5 1≤T≤105),表示数据组数。
每组数据第一行给出两个整数 n , m ( 1 ≤ m ≤ n ≤ 2 × 1 0 5 , ∑ n ≤ 2 × 1 0 5 ) n, m(1 \le m \le n \le 2 \times 10^5, \sum n \le 2 \times 10 ^ 5) n,m(1≤m≤n≤2×105,∑n≤2×105),分别表示骨牌总数与最多可推倒骨牌数。
第二行依次给出 n n n个整数,表示骨牌高度 h i ( 1 ≤ h i ≤ 1 0 9 ) h_{i}(1 \le h_{i} \le 10^9) hi(1≤hi≤109)
第二行依次给出 n n n个不同整数,表示骨牌高度位置 x i ( 1 ≤ x i ≤ 1 0 9 ) x_{i}(1 \le x_{i} \le 10^9) xi(1≤xi≤109)
输出
每组测试数据输出一个整数,表示骨牌最多倒塌数。
测试样例
input
3
6 1
1 1 1 3 2 1
4 3 2 7 9 11
6 2
1 1 1 3 2 1
4 3 2 7 9 11
5 4
1 4 1 1 2
1 2 3 6 8
output
3
6
5
分析
- 要使答案最大,我们应该每次都推倒能连续倒的最大的那个,那么就可以用贪心
- 我们可以先将原数组进行排序,然后从后往前依次处理比较方便,当当前的 x i + h i ≥ x i + 1 x_{i} + h_{i} \ge x_{i + 1} xi+hi≥xi+1,说明能往后连着倒,此时保存新的 x i x_{i} xi 和 个数 c n t cnt cnt 即可,然后再取 m m m次最大的 c n t cnt cnt
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
using pii = std::pair<int, int>;
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<pii> v(n);
for(int i = 0; i < n; i++) {
std::cin >> v[i].second;
}
for(int i = 0; i < n; i++) {
std::cin >> v[i].first;
}
std::sort(all(v));
std::vector<pii> stk;
while(v.size()) {
auto [x, h] = v.back();
v.pop_back();
int cnt = 1;
while(stk.size() && x + h >= stk.back().second) {
cnt += stk.back().first;
stk.pop_back();
}
stk.push_back({cnt, x});
}
std::sort(all(stk));
int ans = 0;
while(stk.size() && m > 0) {
ans += stk.back().first;
stk.pop_back();
m--;
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int t;
std::cin >> t;
while(t--) {
solve();
}
return 0;
}