• [NOIP2012] 借教室 题解


    题解:

      把题面浓缩一下:给定长度为n的正整数序列,m个区间操作,每次操作是把区间[s,t]内所有的数都减去一个d。求使得序列第一次出现负数的最小操作。

    由于序列随着操作的进行时不断减小的,也就是说,若第i次操作使得序列出现负值,那么对大于i的操作也必定会使得序列出现负值。所以,我们可以二分答案,二分操作数。设当前二分的操作数是i,我们就用差分执行前i个操作,若出现负值,说明我们可以将二分右边界缩小,若无负值,就可以将二分左边界扩大。这样的时间复杂度是O(nlogn)。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000000;
    int n,m,ans;
    
    struct point{
        int d,s,t;
    }p[N+5];
    
    int r[N+5],b1[N+5],b2[N+5];
    
    int read(){
        int x=0;
        char ch=getchar();
        while(ch<'0' || ch>'9') ch=getchar();
        while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return x;
    }
    
    bool check(int x){
        bool flag=false;
        memcpy(b2,b1,sizeof(b1));
        for(int i=1;i<=x;++i){
            b2[p[i].s]-=p[i].d;
            b2[p[i].t+1]+=p[i].d;
        }
        for(int i=1;i<=n;++i){
            b2[i]+=b2[i-1];
            if(b2[i]<0){
                flag=true;
                break;
            }
        }
        return flag;
    }
    
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i) r[i]=read();
        for(int i=1;i<=m;++i) p[i].d=read(),p[i].s=read(),p[i].t=read();
        for(int i=1;i<=n;++i) b1[i]=r[i]-r[i-1];
        int l=1,r=n;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid)){
                ans=mid;
                r=mid-1;
            }else l=mid+1;
        }
        if(!ans) printf("0
    ");
        else printf("-1
    %d",ans);
        return 0;
    }
  • 相关阅读:
    【3】hexo+github搭建个人博客的主题配置
    【2】hexo+github搭建个人博客的简单使用
    每日思考(2020/05/06)
    每日思考(2020/05/05)
    每日思考(2020/03/27)
    文件和异常
    每日思考(2020/03/24)
    图形用户界面和游戏开发
    每日思考(2020/03/19)
    面向对象进阶
  • 原文地址:https://www.cnblogs.com/Asika3912333/p/11735598.html
Copyright © 2020-2023  润新知