codeforces1468D Firecrackers

探讨了一个关于小混混与警察在网格图上的追逐问题,小混混利用有限的鞭炮数在警察追捕过程中最大化爆炸数。通过分析,最优策略是根据鞭炮爆炸时间顺序进行贪心分配,确保每个爆炸时间最早的鞭炮能在关键时刻发挥作用。算法复杂度为O(nlogn)。
摘要由CSDN通过智能技术生成

题目链接

题目:

有一个 1 × n 1 \times n 1×n的网格图,小混混在第 a a a个格子里,警察在第 b b b个格子里,小混混有 m m m个鞭炮,第 i i i个鞭炮会在扔出去后 s i s_i si秒爆炸。每秒钟会发生按顺序发生以下三件事情直到小混混被警察抓住:
(1)小混混选择移动一格或者扔一个鞭炮
(2)应在这一秒爆炸的鞭炮爆炸
(3)警察向小混混的方向移动一格
问小混混被抓到之前最多可以使多少个鞭炮爆炸。
( 2 ≤ n ≤ 1 0 9 , 1 ≤ m ≤ 2 × 1 0 5 , 1 ≤ a , b ≤ n , a ≠ b ) (2 \le n \le 10^9,1 \le m \le 2 \times 10^5,1 \le a,b \le n,a \ne b) (2n109,1m2×105,1a,bn,a=b)

题解:

下面只讨论 a < b a<b a<b的情况, a > b a>b a>b的情况同理。
最贪的情况是小混混在第1个格子被警察抓住,在这种情况下小混混会在 b − 1 b-1 b1秒被抓住,他需要移动 a − 1 a-1 a1格,所以他最多可以扔 min ⁡ { m , b − a − 1 } \min\{m,b-a-1\} min{m,ba1}个鞭炮。考虑他是怎么扔这些鞭炮的,他肯定会选择 s s s值尽量小的扔,因为这样可以尽快地爆炸。且可以发现他扔的鞭炮一定全部爆炸,不然没有爆炸的鞭炮就没有必要扔,还白白占了1秒的时间。将鞭炮按 s s s值从小到大排序,基于上面的两个结论,那么他扔的鞭炮一定是一个前缀。现在的问题就变成了找到一个最长的前缀,满足这些鞭炮都可以爆炸,那么我们可以二分前缀长度 k k k,然后 c h e c k check check这个前缀的鞭炮能不能都爆炸。 c h e c k check check的时候我们考虑贪心,当第 i i i个扔爆炸所需时间为 s j s_j sj的鞭炮时,这个鞭炮会在 i + s j i+s_j i+sj的时候爆炸,那么我们就要安排一个扔鞭炮的顺序,使 i + s j i+s_j i+sj的最大值最小。最优策略为第一个扔 s s s值最大的,第二个扔 s s s值次大的,依此类推,为什么这样是最优的?考虑在这个顺序下将两个鞭炮的顺序互换,两个鞭炮所需的爆炸时间分别为 s i , s j ( s i < s j ) s_i,s_j(s_i<s_j) si,sj(si<sj),它们原先所排在的位置分别为 p 1 , p 2 p_1,p_2 p1,p2,那么 p 1 > p 2 p_1>p_2 p1>p2,现在的两者贡献的最大值为 max ⁡ { s i + p 1 , s j + p 2 } \max\{s_i+p_1,s_j+p_2\} max{si+p1,sj+p2},互换顺序后,最大值为 max ⁡ { s i + p 2 , s j + p 1 } = s j + p 1 \max\{s_i+p_2,s_j+p_1\}=s_j+p_1 max{si+p2,sj+p1}=sj+p1,而 s j + p 1 > s i + p 1 , s j + p 1 > s j + p 2 s_j+p_1>s_i+p_1,s_j+p_1>s_j+p_2 sj+p1>si+p1,sj+p1>sj+p2,所以 max ⁡ { s i + p 2 , s j + p 1 } > max ⁡ { s i + p 1 , s j + p 2 } \max\{s_i+p_2,s_j+p_1\}>\max\{s_i+p_1,s_j+p_2\} max{si+p2,sj+p1}>max{si+p1,sj+p2},所以互换任意两个鞭炮的顺序后,最大值不会变小。所以这种顺序是最优的, c h e c k check check一下这种顺序下的最大值是否小于等于 b − 1 b-1 b1即可。

复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=2e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int t,n,m,a,b;
int s[maxn];
int ck1(int x){
	int rem=b-2,f=1;
	for(int i=x;i>=1;i--){
		if(s[i]>rem){
			f=0;
			break;
		}
		rem--;
	}
	return f;
}
int ck2(int x){
	int rem=n-b-1,f=1;
	for(int i=x;i>=1;i--){
		if(s[i]>rem){
			f=0;
			break;
		}
		rem--;
	}
	return f;
}
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d%d",&n,&m,&a,&b);
		for(int i=1;i<=m;i++){
			scanf("%d",&s[i]);
		}
		sort(s+1,s+m+1);
		if(a<b){
			int tim=b-a-1;
			int l=1,r=min(tim,m),ans=0;
			while(l<=r){
				int m=(l+r)>>1;
				if(ck1(m)){
					ans=m;
					l=m+1;
				}
				else{
					r=m-1;
				}
			}
			printf("%d\n",ans);
		}
		else{
			int tim=a-b-1;
			int l=1,r=min(tim,m),ans=0;
			while(l<=r){
				int m=(l+r)>>1;
				if(ck2(m)){
					ans=m;
					l=m+1;
				}
				else{
					r=m-1;
				}
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值