题目:
有一个
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)
(2≤n≤109,1≤m≤2×105,1≤a,b≤n,a=b)
题解:
下面只讨论
a
<
b
a<b
a<b的情况,
a
>
b
a>b
a>b的情况同理。
最贪的情况是小混混在第1个格子被警察抓住,在这种情况下小混混会在
b
−
1
b-1
b−1秒被抓住,他需要移动
a
−
1
a-1
a−1格,所以他最多可以扔
min
{
m
,
b
−
a
−
1
}
\min\{m,b-a-1\}
min{m,b−a−1}个鞭炮。考虑他是怎么扔这些鞭炮的,他肯定会选择
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
b−1即可。
复杂度: 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;
}