牛客小白月赛 99(A - E)

传送门

A - 材料打印

原题

小Y今天去打印毕业材料。这些材料中有 a 页既可以黑白打印也可以彩印,还有 b 页必须彩印。黑白打印一页需要 x 元,彩色打印一页需要 y 元,请计算他最少需要多少钱?

输入

第一行给出一个整数 T T T( 1 ≤ T ≤ 1 0 5 1 \le T \le 10^5 1T105)表示数据组数
对于每组测试数据,给出一行共四个整数 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 1a,b,x,y109)

输出

每组测试数据输出一行,一个整数,表示小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 1T105)下面为 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 1T5×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(1n2×105,n2105),表示总人数
第二行给出 n 个整数 a i ( 2 ≤ a i ≤ 1 0 9 ) a_{i}(2 \le a_{i} \le 10^9) ai(2ai109),依次表示每个人两次眨眼的间隔时间。

输出

每组测试数据输出一个整数,表示最小等待时间。

测试样例

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 1T105),表示数据组数。
每组数据第一行给出两个整数 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(1mn2×105,n2×105),分别表示骨牌总数与最多可推倒骨牌数。
第二行依次给出 n n n个整数,表示骨牌高度 h i ( 1 ≤ h i ≤ 1 0 9 ) h_{i}(1 \le h_{i} \le 10^9) hi(1hi109)
第二行依次给出 n n n个不同整数,表示骨牌高度位置 x i ( 1 ≤ x i ≤ 1 0 9 ) x_{i}(1 \le x_{i} \le 10^9) xi(1xi109)

输出

每组测试数据输出一个整数,表示骨牌最多倒塌数。

测试样例

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+hixi+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;
}
  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值