[二分] 洛谷P1083 借教室 (二分答案+差分)

题目

LP1083

思路

二分答案

首先可以看出,最后答案ans存在明显的单调性,这个单调性是1或0的单调性,有点特殊,大概是这样的:
这里写图片描述
所以就可以二分答案,二分出来的答案应该满足这样的规律:对于ans个订单,教室够借。对于ans+1个订单,教室不够借。
这里给出二分的模板:(来自WYF)
这里写图片描述
所有的二分都应该是下面两种的一个,不能出现类似于这样的二分:

mid = (l+r) >> 1
if(...) return mid;
if(...) l = mid+1;
else r = mid-1;

另外应该注意的是,对于模板的第二种情况(本题就是),应该mid=(l+r+1) >> 1而不是直接mid = (l+r) >> 1,原因是比如l = 1, r = 2, calc(1) = true的情况就会进入死循环。

clac(i)的计算

1.考虑暴力,对于m个区间,每个区间对区间内的每个元素加d[i]:
这里写图片描述
复杂度是: O ( n m )
2.差分:
像本题这样,有一个序列a,不断对区间加同一个数,最后求出数组每个位置的值,这种特殊的问题,可以用差分来解决。差分就相当于是前缀和的逆运算,本题只需将序列a先差分,然后进行差分的“区间加同一个数操作”,最后再前缀和回来即可。
代码是这样的:

// 由于本题序列初始都为0,所以不用先差分
_rep(i,1,mid){  // 差分的“区间加同一个数操作”
        cf[s[i]] += d[i];
        cf[t[i]+1] -= d[i];
    }
_rep(i,1,n) sum[i] = sum[i-1]+cf[i];  // 求一次前缀和,就回来了

这里假设有区间[s,t],需要给区间内每个数加d。那么只需给差分好的数组cf,cf[s]+=d,cf[t+1]-=d即可。(注意这里是t+1不是t)
复杂度: O ( n + m )


所以本题总的复杂度就是, O ( l o g n ( n + m ) ) = O ( n l o g n )

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = a; i<b; i++)
#define _rep(i,a,b) for(int i = a; i<=b; i++)
#define ll long long
using namespace std;

const int maxn = 1000000+100;
int n, m;
ll r[maxn], d[maxn], s[maxn], t[maxn];

ll cf[maxn], sum[maxn];
bool calc(int mid){
    memset(cf,0,sizeof(cf));
    memset(sum,0,sizeof(sum));
    _rep(i,1,mid){
        cf[s[i]] += d[i];
        cf[t[i]+1] -= d[i];
    }
    _rep(i,1,n) sum[i] = sum[i-1]+cf[i];
    bool canbe = true;
    _rep(i,1,n) if(sum[i] > r[i]) canbe = false;
    return canbe;
}

int main(){
    scanf("%d%d",&n,&m);
    _rep(i,1,n) scanf("%lld",&r[i]);
    ll dd,ss,tt;
    _rep(i,1,m){
        scanf("%lld%lld%lld",&dd,&ss,&tt);
        d[i] = dd; s[i] = ss; t[i] = tt;
    }

    int L = 0, R = m;
    while(L<R){
        int mid = (L+R+1)>>1;    // 注意此处+1的特殊情况 
        if (calc(mid)) L = mid;
        else R = mid-1;
    }

    if (L == m) printf("0\n");
    else printf("-1\n%d\n",L+1);

    return 0;
}

//

还有一个更简单的差分代码实现,但不是很直观:
(只用了一个cf数组)

bool calc(int mid){
    memset(cf,0,sizeof(cf));
    _rep(i,1,mid){
        cf[s[i]] += d[i];
        cf[t[i]+1] -= d[i];
    }
    _rep(i,1,n) cf[i] = cf[i-1]+cf[i];
    bool canbe = true;
    _rep(i,1,n) if(cf[i] > r[i]) canbe = false;
    return canbe;
}

猜你喜欢

转载自blog.csdn.net/icecab/article/details/81486412