题面
题目描述
高三数学作业总共有n道题目要写(其实是抄),编号1…n,抄每道题所花时间不一样,抄第i题要花a[i]分钟。由于西西还要准备NOIP,显然不能成天做数学作业。所以西西决定只用不超过t分钟时间抄这个,因此必然有空着的题。每道题要么不写,要么抄完,不能写一半。一段连续的空题称为一个空题段,它的长度就是所包含的题目数。这样应付自然会引起数学老师的愤怒。数学老师发怒的程度(简称发怒度)等于最长的空题段长度。
现在,西西想知道他在这t分钟内写哪些题,才能够尽量降低数学老师的发怒度。由于西西很聪明,你只要告诉他发怒度的数值就可以了,不需输出方案。(Someone:那么西西怎么不自己写程序?西西:我还在抄别的科目的作业……)
输入
第一行为两个整数n,t,代表共有n道题目,t分钟时间。
以下一行,为n个整数,依次为a[1], a[2],… a[n],意义如上所述。
输出
仅一行,一个整数w,为最低的发怒度。
样例输入
17 11
6 4 5 2 5 3 4 5 2 3 4 5 2 3 6 3 5
样例输出
3
数据范围
对于60%数据 n<=2000
对于100%数据 0<n<=50000,0<a[i]<=3000,0<t<=100000000
样例解释
分别写第4,6,10,14题,共用时2+3+3+3=11分钟。空题段:1-3(长度为3), 5-5(1), 7-9(3),
11-13(3), 15-17(3)。所以发怒度为3。可以证明,此数据中不存在使得发怒度<=2的方案。
题解
这题还算是比较水的,一看到求发怒度的最小值,立马就想到了二分,而此题虽然可以用单调队列优化一下在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的时间复杂度内跑出来,但蒟蒻我不会,所以我想到的是线段树。
显然对于第 i i i个位置,它最多能由第 i − m i d − 1 i-mid-1 i−mid−1个位置转移过来,所以可以考虑线段树,这题需要用到单点查询,区间修改。
总时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
代码
#include<bits/stdc++.h>
using namespace std;
int i,j,n,m,k,l,o,p,r,sgt[200005],t[50005];
int ql,qr;
#define mid (l+r)/2
void find(int le,int ri,int lf,int rg,int w)//区间查询
{
if (le==lf&&ri==rg)
{
o=max(o,sgt[w]);
return;
}
int mi=(le+ri)/2;
if (rg<=mi)
{
find(le,mi,lf,rg,w*2);
}
else
{
if (mi<lf)
{
find(mi+1,ri,lf,rg,w*2+1);
}
else
{
find(le,mi,lf,mi,w*2);
find(mi+1,ri,mi+1,rg,w*2+1);
}
}
}
void ins(int le,int ri,int mb,int w)//单点插入
{
if (le==ri)
{
sgt[w]=o-t[i];
return;
}
int mi=(le+ri)/2;
if (mb<=mi)
{
ins(le,mi,mb,w*2);
}
else
{
ins(mi+1,ri,mb,w*2+1);
}
sgt[w]=max(sgt[w*2],sgt[w*2+1]);
}
int main()
{
scanf("%d %d",&n,&m);
for (i=1;i<=n;i++)
{
scanf("%d",&t[i]);
}
l=0,r=n;
while (l<r)//普通的二分
{
memset(sgt,-15,sizeof sgt);
o=m;
i=0;
ins(0,n,0,1);
for (i=1;i<=n;i++)
{
qr=i-1,ql=i-mid-1,ql=max(0,ql);//每个点能转移的区间范围
o=-123123123;
if (ql<=qr)find(0,n,ql,qr,1);
ins(0,n,i,1);
}
qr=i-1,ql=i-mid-1,ql=max(0,ql);
o=-123123123;
find(0,n,ql,qr,1);//确定解的合法性
if (o<0)
{
l=mid+1;
}
else
{
r=mid;
}
}
printf("%d",l);
}