2023河南萌新联赛第(二)场:河南工业大学

第一题:自动收麦机

题目描述

在游戏MC(我的世界)中,如果小麦碰到水流就会掉落,但是mc中的水不能无限流动,在同一高度一桶水往一个方向流最多可以流动8格(算上本身一格)。

2023河南萌新联赛第(二)场:河南工业大学_第1张图片

但是如果流动过程中遇到了阶梯下降了高度,则从下降的那一格开始重新计算距离,所以根据这个原理可以设计一种自动收小麦机,只需要从最高处倒一桶水就可以把所有小麦变成掉落物。

2023河南萌新联赛第(二)场:河南工业大学_第2张图片

这次小小航也根据这个原理建造了一个自动收小麦机,但是小小航并没有精确的计算台阶的位置,当小小航建造完后发现机器不能一次收集所有小麦,现在已知小小航的收小麦机总长为 n 格 ,水流可以流 k ,每一格上的小麦数量为ai。

共有q次询问(每次询问之间互不影响),每次询问一个整数x,在第x格放一桶水共可以收获多少小麦。(水只会从高的一端流向低的一端

输入描述:

第一行三个整数n,q,k(1≤n,q,k≤105)(1\leq n,q,k \leq 10 ^ 5)(1≤n,q,k≤105)

第二行 n个数表示每格上小麦的数量(0≤数量≤109)(0\leq数量 \leq 10^9)(0≤数量≤109)

第三行n个数表示每格的高度,保证非递减(0≤高度≤109)(0\leq 高度 \leq 10^9)(0≤高度≤109)

接下来q行 每行一个数x,表示在第x格放一桶水(1≤x≤n)(1\leq x \leq n)(1≤x≤n)

输出描述:

q行,每行一个整数,表示能收获的小麦数量。

示例1

输入

4 1 2 1 1 4 5 2 2 2 3 4

4 1 2
1 1 4 5
2 2 2 3
4

输出

10

说明

在第4格放出水流后,水流会流向第3格,由于第3格高度比第4格低,所以水流继续向左流向第2格,因为平地水流只能流2格,所以到达第2格后水流停止,收获的小麦数量为1 + 4 + 5 = 10

输入

5 2 2 1 1 4 5 1 2 2 3 3 4 4 3

5 2 2
1 1 4 5 1
2 2 3 3 4
4
3

输出

9

6

解题思路:

对于每次询问中的一个整数 x,需要计算在第 x 格放置水桶后可以收获多少小麦。

根据题目描述,如果水流遇到了台阶下降了高度,则从下降的那一格开始重新计算距离,因此我们可以根据 st 数组找到第 x 格往下最远能流到哪一格。然后根据前缀和数组 sum,计算第 x 格和最远能流到的那一格之间的小麦数量。

具体代码实现:首先读入 n、q 和 k,然后通过一个循环读入每个位置的高度,并计算出麦田小麦的前缀和。接下来,初始化麦田高度数组 h[0] 为 -1,前一个台阶距离数组 tj[1] 为 1,并使用变量 idx 来记录当前位置距离最左侧的距离。

在另一个循环中,依次读入每个位置的高度,并根据相邻高度的差异来更新前一个台阶的距离数组 tj 和最远流到的位置数组 st。如果当前位置的前一个台阶距离小于等于 k,则表示可以流入下一个台阶,将下一个台阶的距离设置为上一个台阶的距离。否则,将下一个台阶的距离设置为当前位置与最左侧的距离减去 k。

最后,在一个循环中,依次读入每个询问的位置 x,并输出该位置的小麦前缀和减去最左侧位置的小麦前缀和。

#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 5, M = 1e5 + 5, INF = 0x3f3f3f3f;
int n, q, k;
ll h[N], sum[N]; // 麦田的高度和麦田小麦的前缀和
int tj[N], st[N]; // 前一个台阶的距离和最远流到的位置
int main() {
	cin >> n >> q >> k;
	for (int i = 1; i <= n; i++) {
		int x;
		cin >> x; // 求前缀和
		sum[i] = sum[i - 1] + x;
	}
	h[0] = -1;  // 麦田的高度
	tj[1] = 1;  // 前一个台阶的距离
	int idx = 0; // 记录距前一个台阶的距离
	for (int i = 1; i <= n; i++) {
		cin >> h[i];
		if (h[i] != h[i - 1]) tj[i] = idx = 1;
		else tj[i] = idx;
		idx++;
		if (tj[i] < k) st[i] = st[i - 1]; // 能够流入下一个台阶
		else st[i] = i - k + 1; // 无法流入下一个台阶
	}
	while (q--) {
		int x;
		cin >> x;
		cout << sum[x] - sum[st[x] - 1] << endl;
	}
	return 0;
}

固执的RT

题目描述

    RT你的帮助下取走了足够多的树枝,并用木制的马车拉着树枝去与人马决一死战。但他忘了一件事,人马是会喷火的。在战斗进行到一半时,卑鄙的人马用火焰将RT所有树枝烧光了。一般人可能已经放弃了,但固执的RT不愿意放弃。他决定再去收集树枝,和人马进行第二次战斗。

    RT现在收集了n个树枝,第i个树枝的攻击力为ai。RT如果想打败人马,收集到的树枝攻击力之和至少为m。请你帮RT判断他现在收集到的树枝是否足够打败人马。

    如果可以请输出“YES”, 否则输出“NO”(输出不带引号)。

输入描述:


第一行两个整数n,m。分别是树枝的数量和RT需要收集的树枝攻击力之和的最低要求。(1≤\leq≤n≤\leq≤1e5, 1≤\leq≤m≤\leq≤1e9)

第二行n个正整数,代表n个树枝的攻击力。(1≤\leq≤ai≤\leq≤1e9)

输出描述:

一行,如果RT收集到的树枝可以击败人马,输出“YES”,否则输出“NO”

示例1

输入

5 9

3 6 8 7 4

输出

YES

示例2

输入

5 9

1 1 1 1 1

输出

NO

解题思路:

 前缀和统计前n个树枝攻击力的总和,然后与m作比较即可。

#include
#include
using namespace std;
long long n,m,x;
long long sum[100005];
int main(){
    cin >> n >> m;
    for(int i=1; i<=n; i++){
        cin >> x;
        sum[i] = x + sum[i-1];
    }
    if(sum[n] >=m ) cout << "YES";
    else cout << "NO";
}

释怀的RT​​​​​​

题目描述 

    又一次收集完树枝后,RT做了防火工作,这次RT成功的使用了树枝战胜了人马。经过了长时间的痛苦折磨,RT释怀了并决定去海拉卢达陆上狩猎岩石巨人,用岩石巨人的心岩照亮心形湖来祭奠他还未开始就结束的爱情。

    假设心形湖由1××n个方格构成,RT在每个方格上放了一个心岩,每个心岩有一个照亮范围x,代表着这块心岩可以照亮它左边x个方格和右边x个方格,但不能照亮心岩所在的方格(假如一个心岩在第5个方格,x=2,那么他只能照亮第3,4和第6,7个方格),现在请你求出心形湖有多少个方格被照亮。

输入描述:


第一行一个正整数n,心形湖格子的个数。( 1 ≤≤ n ≤≤ 1e6)

第二行n个整数,第i个数表示第i个心岩能照亮的范围。(0  ≤≤��xi​ ≤≤ 1e9)

输出描述:

 一行,一个整数,表示照亮的格子数

示例1

输入

5
0 1 0 0 10

输出

4

说明

前四个方格被最后一个心岩照亮

示例2

输入

5
0 1 0 0 1

输出

3

说明

第一个方格和第三个方格被第二个格子的心岩照亮,第四个方格被第五个格子的心岩照亮,一共有三个格子被照亮

解题思路:

可以将第i个心岩的照亮当成对[i - x, i - 1], [i + 1, i + x]这两个区间的每一个格子加1,最后判断格子是否大于0,如 果大于0,说明至少被1个心岩照亮,否则就没有被照亮。区间用的是差分来进行处理。

#include 
using namespace std;
const int N = 1e6 + 10;
int n, x, pre[N];
void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++) {    / /  差分
		cin >> x;
		pre[max(1, i - x)]++, pre[i]--;
		pre[i + 1]++, pre[min(n + 1, i + x + 1)]--;
	}
	int cnt = 0, now = 0;
	for (int i = 1; i <= n; i++) {
		now += pre[i];
		cnt += now > 0;
	}
	cout << cnt << endl;
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	solve();
	return 0;
}

奶牛的寿命

题目描述 

    上帝养了一头奶牛,但是有一天奶牛偷吃了上帝草药,上帝十分生气于是奶牛必须要去地狱接受惩罚,上帝给了奶牛一个长达n的刑期但是同样允许奶牛对刑期进行最多操作log2(n)+1次,每次操作可以交换刑期的二进制形式下任意的两个位置上的数字,但是不能改变原来的二进制数的位数(不能有前导零),奶牛自然希望可以尽可能多的减刑,请问出奶牛最多可以减少多少刑期?

输入描述:

一个正整数n表示奶牛当前刑期。(n<2^31)

输出描述:

一个整数表示奶牛最多可以减少的刑期。

示例1

输入

14

输出

3

说明

只需要交换第1位和第3位,14(1110)-->11(1011),14-11=3。

解题思路:

标记出输入的十进制转化为二进制后,1出现的次数。然后开始创造重新组合的最大值,因为不含前导零,所以第一项只能是1,剩下的0尽可能的往前面放,1尽可能的往后放。

#include
using namespace std;
int main() {
	int n;
	cin >> n;
	bitset<31> m(n);
	int num = m.count() - 1;//num为二进制一的个数‐1 (因为遍历到0)
	bool flag = 0;//判断有没有出现第一个1
	for (int i = 30; i >= 0; i--) {
		if (i < num)m[i] = 1; // 当没有0的时候,即必须补1
		else if (flag)m[i] = 0;
		if (m[i] & 1) flag = 1;
	}
	cout << n - m.to_ulong();// to_ulong() 是将二进制的数转换成是十进制的(无符号)长整型数
	return 0;
}

你可能感兴趣的:(算法)