小小的埴轮兵团—C语言

题目背景
杖刀偶磨弓是埴轮兵团的首长。
作为埴轮兵长,训练埴轮兵团是很平常的事情。
题目描述
磨弓下达命令让埴轮们站成一行。不妨认为它们站在了一个数轴上,每个埴轮的位置就是它脚下数轴的数字。
磨弓会告诉你,第 i 个埴轮的位置为 a_i 。不保证 {a_i}升序。
数轴的长度是有限制的,具体的范围是 [-k,k] 。也就是说,如果某个埴轮移出了这个范围,它就脱离了这个队列了,并且不会再次回到队列当中。
为了训练埴轮,磨弓给埴轮们下达了 m 个指令,有以下 3 种:
指令 1:**全体埴轮**向数轴的正方向移动 x 个单位长度。
指令 2:**全体埴轮**往数轴的反方向移动 x 个单位长度。
指令 3:依次报数,统计目前队列里一共有多少个埴轮。
但是磨弓发现,埴轮兵团的大小实在是太大了,以至于执行这些操作变得非常缓慢。尽管如此,磨弓仍然希望你告诉她所有指令 3 的结果。
输入格式
第一行共有 3 个整数 n, m, k,含义如题面所示。
第二行共有 n 个整数 a_1, a_2, ..., a_n,表示每个埴轮的位置。
接下来 m 行,有 1 或者 2 个正整数,描述一条指令。首先是一个整数 op ,表示这条指令的类型。
如果 1 ≤ op ≤ 2,接下来还会输入一个整数 x。
输出格式
对于每条指令 3 ,输出一个整数,表示目前还在队列中的埴轮的数目。
样例 #1
样例输入 #1
3 4 3
-1 1 2
2 3
3
1 5
3
样例输出 #1
2
1
提示
样例 1 说明
一共有三个埴轮。初始时,它们的站位分别是 [-1,1,2] 。
- 第一次操作后,所有埴轮向左移动 3 格,位置变成了 [-4,-2,-1]  。第一个埴轮被移出了数轴。
- 第二次操作后,输出当前的埴轮数目,为 2 个。
- 第三次操作后,所有埴轮向右移动 5 格,位置变成了 [3,4] ,第二个埴轮被移出了数轴。
- 第四次操作后,输出当前的埴轮数目,为 1 个。
样例 2, 3
见下发附件。
数据规模与约定
- 对于 30% 的数据,1 ≤ n, m ≤ 5 × 10^3;
- 对于另外 20% 的数据,1\≤ k ≤ 500;
- 对于 100% 的数据,1 ≤ n, m ≤ 3 × 10^5,1 ≤ k, x ≤ 2 × 10^9,-k ≤ a_i ≤ k 。

看到这个题目,我相信大部分人已经看出来这个类似于移动窗口和单调队列问题,但是题目已经说了,不保证a数组中的元素升序排序,但是这有什么关系呢,我们只需要把a数组中元素升序排序不就可以使用单调队列了。

首先将数组元素输出,再进行排序,因为我们要用C语言进行排序,所以使用qsort函数是非常好的选择,比冒泡排序和选择排序会快很多,代码也简单。

代码如下:

long long a[N];

int cmp(const void *a,const void *b) 
{
	return *(long long *)a - *(long long *)b;
}

for(int i=1; i<=n; i++) {
	scanf("%lld",&a[i]);
}
qsort(a+1,n,sizeof(long long),cmp);

然后我们需要弄清楚题目的要求,它需要左右移动,并且移动后要满足在区间里,我们定义初始位置为0,如果右移就加上,再进行判断尾部加上的长度是否大于区间,使用单调队列,如果大于,则出列。如果左移就减去,再进行判断头部加上移动的长度是否小于区间,如果小于,则出列。这样就可以保证队列中的元素都属于区间类,op == 1是右移,op == 2是左移。

//往右移x个单位长度
if(op == 1) {
	w += x;
//如果尾部加上移动的长度大于区间,出列
	while(head <= tail && a[tail] + w > k) tail--;
//往左移x个单位长度
}else if(op == 2) {
	w -= x;
	//如果头部加上移动的长度小于区间,出列
	while(head <= tail && a[head] + w < -k) head++;
}

当op == 3时,输出当前的队列中的埴轮数,就是tail - head + 1,为什么要+1,因为假如队列中有5个元素,那么head = 1,tail = 5,tail - head后少了1,所以要+1,

scanf("%lld",&op);
if(op == 3) {
	printf("%lld\n",tail - head + 1);
	continue;
}
scanf("%lld",&x);

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#define N 300005
long long a[N];

int cmp(const void *a,const void *b) 
{
	return *(long long *)a - *(long long *)b;
}

int main()
{
	long long n,m,k,op,x,w=0;
	scanf("%lld %lld %lld",&n,&m,&k);
	for(int i=1; i<=n; i++) {
		scanf("%lld",&a[i]);
	}
	qsort(a+1,n,sizeof(long long),cmp);
	long long head = 1,tail = n;
	for(int i=1; i<=m; i++) {
		scanf("%lld",&op);
		if(op == 3) {
			printf("%lld\n",tail - head + 1);
			continue;
		}
		scanf("%lld",&x);
		//往右移x个单位长度
		if(op == 1) {
			w += x;
			//如果尾部加上移动的长度大于区间,出列
			while(head <= tail && a[tail] + w > k) tail--;
		//往左移x个单位长度
		}else if(op == 2) {
			w -= x;
			//如果头部加上移动的长度小于区间,出列
			while(head <= tail && a[head] + w < -k) head++;
		}
	}
	return 0;
}

注意,必须要long long,用int不能全部通过,数据太大必须要long long。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值