JZOJ 1481. 偷懒的西西【推荐】

题面

题目描述

高三数学作业总共有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 imid1个位置转移过来,所以可以考虑线段树,这题需要用到单点查询,区间修改。

总时间复杂度 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值