B3938 [语言月赛 202402] 陨石

题目背景

卷王来到了他的好朋友 bj12z_jiasiyuan 的农场,打算在那里过上一晚。但是在看新闻时,他们获得了一个不幸的消息,一场陨石雨就快要降临了……

## 题目描述

bj12z_jiasiyuan 的农场里有 n 间牛棚,编号为 1n。第 i 间牛棚的防御值为 ai

陨石雨将会在 t 秒后来临。在陨石雨来临的时候,会有 m 块陨石撞击牛棚,第 i 块陨石会撞击到第 xi 间牛棚。当一块陨石撞击一间牛棚时,牛棚的防御值会减去 2 点。而当一间牛棚的防御值 ≤0时,牛棚会被破坏。

bj12z_jiasiyuan 有很多补给,每个补给可以给一间牛棚增加1点防御值。幸运的是,卷王可以从一间牛棚瞬移到另一间牛棚(瞬移不需花费任何时间),用补给给牛棚增加防御值。每次补给需要 1 秒的时间。

卷王只有 t 秒种的时间可以出去补给,他希望让被破坏的牛棚越少越好。请你输出最优策略下被保护的牛棚的数量。

输入格式

本题单个测试点内包含多组测试数据。

第一行一个整数 T,代表测试数据组数。接下来 T 组数据,每组数据共三行。

第一行三个整数 n,t,m,分别表示牛棚的数量,卷王补给的时间和陨石的数量。  
接下来一行 n 个整数a1​,a2​,⋯,an​,表示第间牛棚的防御值。  
最后一行 m 个整数 x1​,x2​,⋯,xm,表示第 i 块陨石将会撞击第间牛棚。

 输出格式

输出 T 行,每行一个整数,分别表示每组测试数据中最优策略下被保护的牛棚的数量。

输入 #1  
1
4 3 5
2 1 3 5
3 1 2 4 3

输出 #1
3

输入 #2
4
3 4 2
5 2 4
2 1
3 2 20
6 6 6
1 3 3 1 2 1 1 1 2 2 2 1 1 3 1 2 2 3 3 1
2 0 2
1 2
1 1
5 3 12
4 5797 2 1 1
5 4 3 2 4 4 5 5 5 5 1 1

输出 #2
3
0
1
3

说明/提示

样例 1 解释

一种最优的补给方法是补给 1 号牛棚 1 点防御值,补给 2 号牛棚 2 点防御值。

在这种情况下,各牛棚防御值变化如下,其中蓝色数字代表初始防御值,绿色数字代表补给,红色数字代表陨石撞击:

- 1 号:2+1−2=1
- 2 号:1+2−2=1
- 3 号:3−2−2=−1
- 4 号:5−2=3

有且仅有 3 号牛棚被破坏,可保护三个牛棚。

### 数据规模与约定

对于 100% 的数据,1≤xi​≤n≤5×103,1≤m≤106,0≤t≤106,1≤ai​≤106,1≤T≤5×103。保证单个测试点内所有测试数据 n 的总和不超过 5×104,所有测试数据 m 的总和不超过 3×106。

测试点编号特殊限制
1,2T=1,n=1
3,4T=1,每间牛棚恰好被击中一次
5T=1,1≤xi​≤n≤100
6T=1
71≤xi​≤n≤100
8∼10无特殊限制

#include<bits/stdc++.h>        //万能头文件
using namespace std;
int T,n,t,m,a[5005],x[1000005],ans;
                              //总测试组数T,牛棚总数n,补给总时间t,陨石总数m,所有牛棚的防御值a数组,被陨石撞击的牛棚编号x数组,被保护的牛棚总数的最大值ans
int main(){
	cin>>T;                   //输入总测试组数
	for (int i=1;i<=T;i++) {  //通过循环进行测试
		cin>>n>>t>>m;         //输入牛棚总数,补给总时间,陨石总数
		for (int i=1;i<=n;i++) {
                              //先输入所有牛棚的防御值
			cin>>a[i];        //将它们存进a数组
		}
		for (int i=1;i<=m;i++) {
                              //然后输入被陨石撞击的牛棚编号
			cin>>x[i];        //将它们存进x数组
			a[x[i]]-=2;       //陨石砸一下掉两点防御值,所以把被陨石撞击的牛棚的牛棚的防御值都减二
		}
		sort(a+1,a+n+1);      //将撞击后的所有牛棚的防御值从低到高排序
		ans=0;                //因为包含多组测试数据,所以把ans初始化一下
		for (int i=n;i>=1;i--) {
			                  //因为要保护尽可能多的牛棚,所以要倒着看
			if (t>0&&a[i]<=0&&t>=1-a[i]) {
                              //首先要确保还有补给时间,其次是这个牛棚需要修补,最后是要确保修完之后不会超时
				int b=1-a[i]; //定义b的值为修好编号为a[i]的牛棚的时间(不定义也可以)
				t-=b;         //将补给总时间减去修补时间
				a[i]+=b;      //这个牛棚修好了
			}
			if (a[i]>=1) {    //如果牛棚的防御值大于等于一(没被破坏,成功被保护)
				ans++;        //增加被保护的牛棚总数
			}
			
		}
		cout<<ans<<endl;      //输出,别忘了换行
	}
	
	return 0;
}

 PS:关于为什么要倒着看被砸后的牛棚防御值,是因为要修好更多的牛棚,不能死磕最小的那一个,这样会减少被保护的牛棚总数,这样的话有可能连最小的都补不好。比如说补给时间是50,防御值最小的是-100,但其他都是-1。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值