【JZOJ3463】军训

Description

HYSBZ 开学了!今年HYSBZ 有n 个男生来上学,学号为1…n,每个学生都必须参加军训。在这种比较堕落的学校里,每个男生都会有Gi 个女朋友,而且每个人都会有一个欠扁值Hi。学校为了保证军训时教官不会因为学生们都是人生赢家或者是太欠扁而发生打架事故,所以要把学生们分班,并做出了如下要求:

1.分班必须按照学号顺序来,即不能在一个班上出现学号不连续的情况。

2.每个学生必须要被分到某个班上。

3.每个班的欠扁值定义为该班中欠扁值最高的那名同学的欠扁值。所有班的欠扁值之和不得超过Limit。

4.每个班的女友指数定义为该班中所有同学的女友数量之和。在满足条件1、2、3 的情况下,分班应使得女友指数最高的那个班的女友指数最小。

输入数据保证题目有解。

Solution

首先这题最大值最小,立即二分答案。

一开始我还以为很简单的贪心就能过(然而我还是Too young)……

首先这道题40%的数据,可以想到 O(n2log2n) 的方法(其中 Fi 表示到 i 的最小欠扁值和):

Fi=min(Fj(k=i+1jgimid)+max(hk)(j<ki))

接下来我们观察上面的式子,首先,j只用一个指针模拟即可(显然),然后是 max(hk)(j<ki)) ,我们发现它们太恶心,还有 min(Fj)(1j<i) 等。

到这里,大神纷纷用stl,而蒟蒻的我只能在这打线段树。

首先 max(hk) 是从左到右单调递减的,可以用线段树维护(区间修改,查询),然后是 min(Fj) 可以逐次加入,逐次查询 Fj+max(hk) 的最小值。

于是,我们收获了一棵复杂度较大的线段树。

这样单次二分复杂度降到了 O(nlog2n) ,总复杂度就是 O(nlog22n)

Code

小心点,不要迷失!
时限2s,我用了1.107s……

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 20001
#define M 1001
#define ll long long
#define inf 100000000
using namespace std;
int f[N],g[N],h[N];
struct node
{
    int mh,mf,mz,lazy;
}tr[N*4];
int n,lim;
int p=0;
ll cnt=0;
void put(int v)
{
    if(!tr[v].lazy) return;
    tr[v*2].mh=max(tr[v*2].mh,tr[v].lazy);
    tr[v*2+1].mh=max(tr[v*2+1].mh,tr[v].lazy);
    tr[v*2].mz=tr[v*2].mf+tr[v*2].mh;
    tr[v*2+1].mz=tr[v*2+1].mf+tr[v*2+1].mh;
    tr[v*2].lazy=max(tr[v*2].lazy,tr[v].lazy);
    tr[v*2+1].lazy=max(tr[v*2+1].lazy,tr[v].lazy);
    tr[v].lazy=0;
}
void update(int v)
{
    tr[v].mh=max(tr[v*2].mh,tr[v*2+1].mh);
    tr[v].mf=min(tr[v*2].mf,tr[v*2+1].mf);
    tr[v].mz=min(tr[v*2].mz,tr[v*2+1].mz);
}
int pos=0;
int findpos(int v,int l,int r,int z)
{
    if(l==r) return l;
    int mid=(l+r)/2;
    put(v);
    if(tr[v*2+1].mh>z) return findpos(v*2+1,mid+1,r,z);
    else if(tr[v*2].mh>z) return findpos(v*2,l,mid,z);
    update(v);
    return 0;
}
void find(int v,int l,int r,int x,int y,int z)
{
    if(l==x && r==y)
    {
        if(!pos && tr[v].mh>=z) pos=findpos(v,l,r,z);
        return;
    }
    if(pos) return;
    int mid=(l+r)/2;
    put(v);
    if(x>mid) find(v*2+1,mid+1,r,x,y,z);
    else if(y<=mid) find(v*2,l,mid,x,y,z);
    else
    {
        find(v*2+1,mid+1,r,mid+1,y,z);
        find(v*2,l,mid,x,mid,z);
    }
    update(v);
}
void change(int v,int l,int r,int x,int y,int z)
{
    if(l==x && r==y)
    {
        tr[v].mh=z;
        tr[v].mz=z+tr[v].mf;
        tr[v].lazy=z;
        return;
    }
    int mid=(l+r)/2;
    put(v);
    if(y<=mid) change(v*2,l,mid,x,y,z);
    else if(x>mid) change(v*2+1,mid+1,r,x,y,z);
    else
    {
        change(v*2,l,mid,x,mid,z);
        change(v*2+1,mid+1,r,mid+1,y,z);
    }
    update(v);
}
void insert(int v,int l,int r,int x,int z)
{
    if(l==r && l==x)
    {
        tr[v].mf=z;
        tr[v].mz=z+tr[v].mh;
        return;
    }
    int mid=(l+r)/2;
    put(v);
    if(x<=mid) insert(v*2,l,mid,x,z);
    else insert(v*2+1,mid+1,r,x,z);
    update(v);
}
void findans(int v,int l,int r,int x,int y)
{
    if(l==x && r==y)
    {
        p=min(p,tr[v].mz);
        return;
    }
    int mid=(l+r)/2;
    put(v);
    if(y<=mid) findans(v*2,l,mid,x,y);
    else if(x>mid) findans(v*2+1,mid+1,r,x,y);
    else
    {
        findans(v*2,l,mid,x,mid);
        findans(v*2+1,mid+1,r,mid+1,y);
    }
}
bool check(int x)
{
    memset(tr,0,sizeof(tr));
    int j=1;
    fo(i,1,n)
    {
        if(g[i]-g[i-1]>x) return false;
        while(g[i]-g[j-1]>x) j++; 
        pos=0;
        find(1,1,n,j,i,h[i]);
        change(1,1,n,max(pos+1,j),i,h[i]);
        p=inf;
        findans(1,1,n,j,i);
        f[i]=p;
        if(i<n) insert(1,1,n,i+1,f[i]);
    }
    if(f[n]<=lim) return true;
    return false;
}
int main()
{
    cin>>n>>lim;
    fo(i,1,n) scanf("%d %d",&h[i],&g[i]),g[i]+=g[i-1];
    int l=1,r=g[n];
    while(l<r)
    {
        int mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    cout<<l;
}
posted @ 2016-06-30 18:40  sadstone  阅读(38)  评论(0编辑  收藏  举报