目录:
题目:
分析:
题目是求最大值最小,所以应该二分。
如何判断当前的mid值是否合法呢?
这里用DP实现。
设
表示i之前已经分好班的时候的最小欠扁值。
转移显然:
但是这个DP是
的,会超时。
不过还是可以得30-40分。
优化
设nextinexti表示在i后面第一个
也就是说,在
中的最大欠扁值为
如果一个j,满足
,那么这个
对这个班
的最大欠扁值是没有影响的。既然是这样,就直接跳到
就好了。
代码:
#pragma GCC optimize("3")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
#define LL long long
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
int q[20001],g[20001],next[20001];
int num[20001],z[20001];
LL sum[20001],f[20003],m,l,r,mid,ans;
int n,k,t,top;
int get(int t)
{
int l=t,r=n;
int mi;
while(l<r)
{
mi=(l+r)/2;
if(sum[mi]-sum[t-1]>=mid)
r=mi;
else l=mi+1;
}
return l;
}
bool ok(int t)
{
memset(f,127,sizeof(f));
f[1]=0;
for(int i=1;i<=n;i++)
{
int k=get(i);
int x=i;
int ma=q[x];
if(sum[k]-sum[i-1]>t) k--;
while(x<=k)
{
f[x]=min(f[x],f[i]+ma);
ma=q[x];
x=next[x];
}
f[k+1]=min(f[k+1],f[i]+ma);
}
if(f[n+1]>m) return 0;
else return 1;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
{
q[i]=read();g[i]=read();
sum[i]=sum[i-1]+g[i];
}
top=1;
z[1]=2147483647;
num[1]=n+1;
for(int i=n;i;i--)
{
while(q[i]>=z[top]) top--;
next[i]=num[top];
z[++top]=q[i];
num[top]=i;
}
l=1;r=sum[n];
ans=sum[n];
while(l<=r)
{
mid=(l+r)/2;
if(ok(mid))
{
ans=min(ans,mid);
r=mid-1;
}
else l=mid+1;
}
printf("%lld",ans);
return 0;
}