一本通1603
【题意】: 高二数学《绿色通道》
总共有
n
n
n 道题目要抄,编号
1
…
n
1…n
1…n,抄第
i
i
i 题要花
a
i
a_i
ai 分钟。小Y
决定只用不超过
t
t
t 分钟抄这个,因此必然有空着的题。每道题要么不写,要么抄完,不能写一半。下标连续的一些空题称为一个空题段,它的长度就是所包含的题目数。这样应付自然会引起马老师
的愤怒,最长的空题段越长,马老师越生气。
现在,小Y
想知道他在这
t
t
t 分钟内写哪些题,才能够尽量减轻马老师的怒火。由于小Y
很聪明,你只要告诉他最长的空题段至少有多长就可以了,不需输出方案。
【思路】: 我们发现这题要维护的量有两个:空题段的长度和已花费的时间。如果我们想一次性维护这两个量的话就有一点复杂。
但是题目所求可以抽象成最大值最小,这很容易让我们联想到二分。细细一想,我们可以发现这题的单调性,即可二分性:如果最长空题段的长为
k
k
k时,可以在
t
t
t分钟内抄完,那么如果空题段的长
L
>
k
L>k
L>k,也一定可以在
t
t
t分钟内抄完。
具体地,我们可以二分一个值
m
i
d
mid
mid,表示最长的空题段的长度不超过
m
i
d
mid
mid,我们可以用dp求出此时的最小时间。记
f
[
i
]
f[i]
f[i]表示
i
i
i必抄时的最短时间,则
f
[
i
]
=
m
i
n
{
f
[
j
]
}
+
a
[
i
]
f[i]=min\{f[j]\}+a[i]
f[i]=min{f[j]}+a[i],其中
i
−
m
i
d
≤
j
<
i
i-mid\leq j<i
i−mid≤j<i,可以用单调队列优化它。
【代码】:
const int N=5e4+100;
int q[N],dp[N],a[N],n,T;
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
inline bool check(int mid){
register int h=1,t=1;
for(int i=1;i<=n;i++){
while (h<=t&&q[h]<i-mid-1) h++;
dp[i]=dp[q[h]]+a[i];
while (h<=t&&dp[i]<dp[q[t]]) t--;
q[++t]=i;
}
register int ans=dp[n-mid];
for(int i=n-mid+1;i<=n;i++)
ans=min(ans,dp[i]);
return ans<=T;
}
int l,r,mid,ans;
int main(){
// freopen("t1.in","r",stdin);
n=read();T=read();
for(int i=1;i<=n;i++)
a[i]=read();
l=0;r=n;
while (l<=r){
mid=(l+r)>>1;
if (check(mid)){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%d",ans);
return 0;
}
【时间复杂度】: O ( N × l o g N ) O(N \times logN) O(N×logN)