HYSBZ 开学了!今年HYSBZ 有n 个男生来上学,学号为1…n,每个学生都必须参加军训。在这种比较堕落的学校里,每个男生都会有Gi 个女朋友,而且每个人都会有一个欠扁值Hi。学校为了保证军训时教官不会因为学生们都是人生赢家或者是太欠扁而发生打架事故,所以要把学生们分班,并做出了如下要求:
1.分班必须按照学号顺序来,即不能在一个班上出现学号不连续的情况。
2.每个学生必须要被分到某个班上。
3.每个班的欠扁值定义为该班中欠扁值最高的那名同学的欠扁值。所有班的欠扁值之和不得超过Limit。
4.每个班的女友指数定义为该班中所有同学的女友数量之和。在满足条件1、2、3 的情况下,分班应使得女友指数最高的那个班的女友指数最小。
请你帮HYSBZ 的教务处完成分班工作,并输出女友指数最高的班级的女友指数。
输入数据保证题目有解。
对于100%的数据:1<=n,Gi<=20000,1<=Hi,Limit<=10^7
做这道题的经历可以算是坎坷了。
现在发现了做比赛的另一个困难,本以为能打完的代码打完调调调,代码量越调越多越多越调,恶性循环。。。结果一直耗到比赛结束还是漏洞百出。。。还能说什么,要多多练习,熟能生巧。
扯了一些废话,讲正事。
首先看到双最值,果断二分答案。设欠扁值之和的限制为LimH,二分出来女友指数限制为LimG。
所以这里带了个log,我们的问题变成了如何判定满足LimG的情况下满不满足LimH。
一开始,naive的我以为一个simple的greedy就能A,后来发现了反例,开始有点方。
想DP咯,想啊想,发现基本式子是这样子的,设 f[i] 表示到 i 的最小的欠扁和,则有
很丑很丑,不要介意。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=20010;
int n,ans,limh,h[N],g[N],f[N],q[N],sumg[N];
struct segment
{
int mn,mnf,mnh,mxh,lz;
}a[N*4];
void down(int v)
{
if(a[v].lz==0) return;
int z=a[v].lz;
a[v+v].mxh=max(a[v+v].mxh,z),a[v+v+1].mxh=max(a[v+v+1].mxh,z);
a[v+v].mnh=a[v+v].mxh,a[v+v+1].mnh=a[v+v+1].mxh;
a[v+v].mn=a[v+v].mnf+a[v+v].mnh,a[v+v+1].mn=a[v+v+1].mnf+a[v+v+1].mnh;
a[v+v].lz=max(a[v+v].lz,z),a[v+v+1].lz=max(a[v+v+1].lz,z);
a[v].lz=0;
}
void serch(int v,int l,int r,int z)
{
if(a[v].mxh<z)
{
a[v].mnh=a[v].mxh=z;
a[v].mn=a[v].mnf+z;
a[v].lz=z;
return;
}
if(l==r) return;
down(v);
int mid=(l+r)>>1;
if(a[v+v].mnh<z) serch(v+v,l,mid,z);
if(a[v+v+1].mnh<z) serch(v+v+1,mid+1,r,z);
a[v].mn=min(a[v+v].mn,a[v+v+1].mn);
a[v].mnh=min(a[v+v].mnh,a[v+v+1].mnh);
a[v].mxh=max(a[v+v].mxh,a[v+v+1].mxh);
}
void dfs(int v,int l,int r,int x,int y,int z)
{
if(l==x && r==y)
{
serch(v,l,r,z);
return;
}
down(v);
int mid=(l+r)>>1;
if(y<=mid) dfs(v+v,l,mid,x,y,z);
else
if(x>mid) dfs(v+v+1,mid+1,r,x,y,z);
else
dfs(v+v,l,mid,x,mid,z),dfs(v+v+1,mid+1,r,mid+1,y,z);
a[v].mn=min(a[v+v].mn,a[v+v+1].mn);
a[v].mnh=min(a[v+v].mnh,a[v+v+1].mnh);
a[v].mxh=max(a[v+v].mxh,a[v+v+1].mxh);
}
void modify(int v,int l,int r,int x,int z)
{
if(l==r)
{
a[v].mnf=z,a[v].mn=z+h[l];
a[v].mnh=a[v].mxh=h[l];
return;
}
down(v);
int mid=(l+r)>>1;
if(x<=mid) modify(v+v,l,mid,x,z);
else modify(v+v+1,mid+1,r,x,z);
a[v].mn=min(a[v+v].mn,a[v+v+1].mn);
a[v].mnf=min(a[v+v].mnf,a[v+v+1].mnf);
a[v].mnh=min(a[v+v].mnh,a[v+v+1].mnh);
a[v].mxh=max(a[v+v].mxh,a[v+v+1].mxh);
}
void find(int v,int l,int r,int x,int y)
{
if(l==x && r==y)
{
ans=min(ans,a[v].mn);
return;
}
down(v);
int mid=(l+r)>>1;
if(y<=mid) find(v+v,l,mid,x,y);
else
if(x>mid) find(v+v+1,mid+1,r,x,y);
else
find(v+v,l,mid,x,mid),find(v+v+1,mid+1,r,mid+1,y);
}
bool check(int limg)
{
memset(a,0,sizeof(a));
int l=1;
fo(i,1,n)
{
while(sumg[i]-sumg[l-1]>limg) l++;
modify(1,1,n,i,f[i-1]);
dfs(1,1,n,l,i,h[i]);
ans=2147483647;
find(1,1,n,l,i);
f[i]=ans;
}
if(f[n]>limh) return 0;
return 1;
}
int main()
{
scanf("%d %d",&n,&limh);
int l=0,r=0;
fo(i,1,n)
{
scanf("%d %d",&h[i],&g[i]);
sumg[i]=sumg[i-1]+g[i];
l=max(l,g[i]),r+=g[i];
}
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d",l);
return 0;
}