【JZOJ 3463】 军训 training

Description

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

Analysis

做这道题的经历可以算是坎坷了。
现在发现了做比赛的另一个困难,本以为能打完的代码打完调调调,代码量越调越多越多越调,恶性循环。。。结果一直耗到比赛结束还是漏洞百出。。。还能说什么,要多多练习,熟能生巧。
扯了一些废话,讲正事。
首先看到双最值,果断二分答案。设欠扁值之和的限制为LimH,二分出来女友指数限制为LimG。
所以这里带了个log,我们的问题变成了如何判定满足LimG的情况下满不满足LimH。
一开始,naive的我以为一个simple的greedy就能A,后来发现了反例,开始有点方。
想DP咯,想啊想,发现基本式子是这样子的,设 f[i] 表示到 i 的最小的欠扁和,则有

f[i]=Min(f[j]+MaxH(j+1 to i)),k=j+1ig[k]>LimG

MaxH 表示从 j+1 i H Max
右边那个限制弄一个左端点模拟就好了,重点是左边这一坨恶心的东西。
继续想,想啊想,发现可以用一个实现极极极极猥琐的线段树。
因为那个MaxH是从右到左单调不减的,所以可以找到一个中间点,从中间点到i这一段都修改一下。f[i]就直接查询可行区间中的最优解。做完f[i]之后还要把f[i]加入线段树。
涉及区间查询、修改,单点查询。。。维护还要搞一堆堆神奇的东西。。
不像其他大神们用multiset,又短又快。
实现的时候磕磕绊绊,把==打成=,被>>运算的优先级坑了等等。

Code

很丑很丑,不要介意。

#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;
}

你可能感兴趣的:(dp,线段树,单调性)