zoukankan      html  css  js  c++  java
  • NOIP2012 D2 T2 借教室 线段树 OR 二分法

    题目描述:
    在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

    面对海量租借教室的信息,我们自然希望编程解决这个问题。

    我们需要处理接下来n天的借教室信息,其中第i天学校有r(i)个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为d(j),s(j),t(j),表示某租借者需要从第s(j)天到第t(j)天租借教室(包括第s(j)天和第t(j)天),每天需要租借d(j)个教室。

    我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供d(j)个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

    借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申 请人修改订单。这里的无法满足指从第s(j)天到第t(j)天中有至少一天剩余的教室数量不足d(j)个。

    现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

    输入格式
    第一行包含两个正整数n,m,表示天数和订单的数量。

    第二行包含n个正整数,其中第i个数为r(i),表示第i天可用于租借的教室数量。

    接下来有m行,每行包含三个正整数d(j),s(j),t(j),表示租借的数量,租借开始、结束分别在第几天。

    每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。

    输出格式
    如果所有订单均可满足,则输出只有一行,包含一个整数 0。否则(订单无法完全满足) 输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。

    测试样例
    样例输入

    4 3
    2 5 4 3
    2 1 3
    3 2 4
    4 2 4
    样例输出

    -1
    2
    样例说明

    第 1 份订单满足后,4 天剩余的教室数分别为 0,3,2,3。第 2 份订单要求第 2 天到 第 4 天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第 2 个申请人修改订单。

    数据范围与提示
    对于 10%的数据,有1 ≤ n,m ≤ 10;

    对于 30%的数据,有1 ≤ n,m ≤ 1000;

    对于 70%的数据,有1 ≤ n,m ≤ 10^5;

    对于 100%的数据,有1 ≤ n,m ≤ 10^6,0 ≤ r(i),d(j) ≤ 10^9,1 ≤ s(j) ≤ t(j) ≤ n。




    思路:
    1.
    线段树 维护区间的最小值,每次修改可以运用打标记的方法节省时间(关于节省时间 我后面有话说)。。。
    2.
    二分?(可惜并不会写,不知道怎么二分)。
    晚上补了个二分的程序。
    (若要看正解,请直接翻至页面底部。。 中间废话多)

    只能写线段树了。。/(ㄒoㄒ)/~~
    首先在TYVJ上写了一个不加lazy标记的线段树

    //By SiriusRen
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int tree[1000005],xx,yy,zz,lazy[1000005],n,m;
    void build(int l,int r,int pos){
        if(l==r){scanf("%d",&tree[pos]);return;}
        int mid=(l+r)/2;
        build(l,mid,pos*2);build(mid+1,r,pos*2+1);
        tree[pos]=min(tree[pos*2],tree[pos*2+1]);
    }
    int update(int l,int r,int pos){
        if(l==r)return tree[pos]-=zz;
        int mid=(l+r)/2;
        if(mid<xx)return update(mid+1,r,pos*2+1);
        else if(mid>=yy)return update(l,mid,pos*2);
        else return tree[pos]=min(update(l,mid,pos*2),update(mid+1,r,pos*2+1));
        tree[pos]=min(tree[pos*2],tree[pos*2+1]);
    }
    int main(){
        scanf("%d%d",&n,&m);
        build(1,n,1);
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&zz,&xx,&yy);
            if(update(1,n,1)<0){printf("-1
    %d",i);return 0;}
        }
        puts("0");
        return 0;
    }

    后果很惨烈。。。
    这里写图片描述


    改了改 改了很久很久(期间经过无数次崩溃),改成了加Lazy标记的线段树。

    //By SiriusRen
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int tree[10000005],xx,yy,zz,lazy[10000005],n,m;
    void build(int l,int r,int pos){
        if(l==r){scanf("%d",&tree[pos]);return;}
        int mid=(l+r)/2;
        build(l,mid,pos*2);build(mid+1,r,pos*2+1);
        tree[pos]=min(tree[pos*2],tree[pos*2+1]);
    }
    void push_down(int pos){
        tree[pos]-=lazy[pos];
        lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
        lazy[pos]=0;
    }
    void update(int l,int r,int pos){
        if(lazy[pos])push_down(pos);
        if(l>yy||r<xx)return;
        if(l>=xx&&r<=yy){lazy[pos]+=zz;push_down(pos);return;}
        if(lazy[pos])push_down(pos);
        int mid=(l+r)/2;
        update(l,mid,pos*2);update(mid+1,r,pos*2+1);
        tree[pos]=min(tree[pos*2],tree[pos*2+1]);
    
    }
    int main(){
        scanf("%d%d",&n,&m);
        build(1,n,1);
        for(int ii=1;ii<=m;ii++){
            scanf("%d%d%d",&zz,&xx,&yy);update(1,n,1);
            if(tree[1]<0){
                printf("-1
    %d",ii);return 0;}
        }
        puts("0");
        return 0;
    }

    这里写图片描述
    还是T了一个点。。



    怎么办呢 怎么办呢(此时我的内心接近崩溃。。)
    队长过来了,,说:“你加个读入优化吧 balabala”

    //By SiriusRen
    #include <cstdio>
    using namespace std;
    int min(int x,int y){return x<y?x:y;}
    int tree[10000005],xx,yy,zz,lazy[10000005],n,m;
    void build(int l,int r,int pos){
        if(l==r){scanf("%d",&tree[pos]);return;}
        int mid=(l+r)/2;
        build(l,mid,pos*2);build(mid+1,r,pos*2+1);
        tree[pos]=min(tree[pos*2],tree[pos*2+1]);
    }
    void push_down(int pos){
        tree[pos]-=lazy[pos];
        lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
        lazy[pos]=0;
    }
    void update(int l,int r,int pos){
        if(lazy[pos])push_down(pos);
        if(l>yy||r<xx)return;
        if(l>=xx&&r<=yy){lazy[pos]+=zz;push_down(pos);return;}
        if(lazy[pos])push_down(pos);
        int mid=(l+r)/2;
        update(l,mid,pos*2);update(mid+1,r,pos*2+1);
        tree[pos]=min(tree[pos*2],tree[pos*2+1]);
    }
    int get(){
        int x=0;char p=getchar();
        while(p<'0'||p>'9')p=getchar();
        while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();
        return x;
    }
    int main(){
        scanf("%d%d",&n,&m);
        build(1,n,1);
        for(int ii=1;ii<=m;ii++){
            zz=get();xx=get();yy=get();update(1,n,1);
            if(tree[1]<0){
                printf("-1
    %d",ii);return 0;}
        }
        puts("0");
        return 0;
    }

    我就加了一个。 成功水过TYVJ的数据。【鼓掌!】
    这里写图片描述
    队长又过来了,说:“Vijos数据很强,你要不试试。。”
    当时Vijos已挂,晚上回家就试了试。
    这里写图片描述

    果断挂了。【桑心】




    各种卡评测机常数。。。。。。

    //By SiriusRen
    #include <cstdio>
    using namespace std;
    inline int min(int x,int y){return x<y?x:y;}
    int tree[10000005],xx,yy,zz,lazy[10000005],n,m;
    void build(int l,int r,int pos){
        if(l==r){scanf("%d",&tree[pos]);return;}
        int mid=(l+r)/2;
        build(l,mid,pos*2);build(mid+1,r,pos*2+1);
        tree[pos]=min(tree[pos*2],tree[pos*2+1]);
    }
    void update(int l,int r,int pos){
        if(lazy[pos]){
            tree[pos]-=lazy[pos];
            lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
            lazy[pos]=0;
        }
        if(l>yy||r<xx)return;
        if(l>=xx&&r<=yy){lazy[pos]+=zz; tree[pos]-=lazy[pos];
        lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
        lazy[pos]=0;return;}
        if(lazy[pos]){
            tree[pos]-=lazy[pos];
            lazy[pos*2]+=lazy[pos];lazy[pos*2+1]+=lazy[pos];
            lazy[pos]=0;
        }
        int mid=(l+r)/2;
        update(l,mid,pos*2);update(mid+1,r,pos*2+1);
        tree[pos]=min(tree[pos*2],tree[pos*2+1]);
    }
    inline int get(){
        int x=0;char p=getchar();
        while(p<'0'||p>'9')p=getchar();
        while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();
        return x;
    }
    int main(){
        register int n=0;register char p=getchar();
        while(p<'0'||p>'9')p=getchar();
        while(p>='0'&&p<='9')n=n*10+p-'0',p=getchar();
        register int m=0;p=getchar();
        while(p<'0'||p>'9')p=getchar();
        while(p>='0'&&p<='9')m=m*10+p-'0',p=getchar();
        build(1,n,1);
        for(int ii=1;ii<=m;ii++){
            xx=yy=zz=0;
            p=getchar();
            while(p<'0'||p>'9')p=getchar();
            while(p>='0'&&p<='9')zz=zz*10+p-'0',p=getchar();
            p=getchar();
            while(p<'0'||p>'9')p=getchar();
            while(p>='0'&&p<='9')xx=xx*10+p-'0',p=getchar();
            p=getchar();
            while(p<'0'||p>'9')p=getchar();
            while(p>='0'&&p<='9')yy=yy*10+p-'0',p=getchar();
            update(1,n,1);
            if(tree[1]<0){printf("-1
    %d",ii);return 0;}
        }
        puts("0");
    }

    把get函数手动内置了一下。。。
    这里写图片描述
    哈哈AC了



    网上找了个别的题库交了交。
    这里:http://syzoj.com/problem/14(数据真心良心)
    这里写图片描述
    就光荣的挂了…….挂了……挂了。。。!!!
    然后呢,我就各种改内置,一通乱搞。

    //By SiriusRen
    #include <cstdio>
    using namespace std;
    int tree[10000005],xx,yy,zz,lazy[10000005];
    inline void build(int l,int r,int pos){
        if(l==r){
            register char p=getchar();
            while(p<'0'||p>'9')p=getchar();
            while(p>='0'&&p<='9')tree[pos]=tree[pos]*10+p-'0',p=getchar();
            return;
        }
        register int mid=(l+r)>>1,lson=pos<<1,rson=pos+pos+1;
        build(l,mid,lson);
        build(mid+1,r,rson);
        tree[pos]=tree[lson]<tree[rson]?tree[lson]:tree[rson];
    }
    inline void update(int l,int r,int pos){
        register int lson=pos<<1,rson=pos+pos+1;
        if(lazy[pos]){
            tree[pos]-=lazy[pos];
            lazy[lson]+=lazy[pos];
            lazy[rson]+=lazy[pos];
            lazy[pos]=0;
        }
        if(l>yy||r<xx)return;
        if(l>=xx&&r<=yy){
            lazy[pos]+=zz;
            tree[pos]-=lazy[pos];
            lazy[lson]+=lazy[pos];
            lazy[rson]+=lazy[pos];
            lazy[pos]=0;
            return;
        }
        if(lazy[pos]){
            tree[pos]-=lazy[pos];
            lazy[lson]+=lazy[pos];
            lazy[rson]+=lazy[pos];
            lazy[pos]=0;
        }
        register int mid=(l+r)>>1;
        update(l,mid,lson);
        update(mid+1,r,rson);
        tree[pos]=tree[lson]<tree[rson]?tree[lson]:tree[rson];
    }
    int main(){
        register int n=0,m=0;
        register char p=getchar();
        while(p<'0'||p>'9')p=getchar();
        while(p>='0'&&p<='9')n=n*10+p-'0',p=getchar();
        p=getchar();
        while(p<'0'||p>'9')p=getchar();
        while(p>='0'&&p<='9')m=m*10+p-'0',p=getchar();
        build(1,n,1);
        for(int ii=1;ii<=m;ii++){
            xx=yy=zz=0;
            p=getchar();
            while(p<'0'||p>'9')p=getchar();
            while(p>='0'&&p<='9')zz=zz*10+p-'0',p=getchar();
            p=getchar();
            while(p<'0'||p>'9')p=getchar();
            while(p>='0'&&p<='9')xx=xx*10+p-'0',p=getchar();
            p=getchar();
            while(p<'0'||p>'9')p=getchar();
            while(p>='0'&&p<='9')yy=yy*10+p-'0',p=getchar();
            update(1,n,1);
            if(tree[1]<0){printf("-1
    %d",ii);return 0;}
        }
        puts("0");
    }

    这里写图片描述
    卡时卡过去了哈哈哈哈

    总结了几个小技巧:
    1.
    读入优化(粘个函数):

    int get(){
        int x=0;char p=getchar();
        while(p<'0'||p>'9')p=getchar();
        while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();
        return x;
    }

    2.
    少调用函数,节省函数调用的时间。必要时可以手动内置(Code Length什么的在AC面前都是浮云),手写min函数、max函数什么的,可以快很多。
    3.
    关于乘除法
    多用位移操作,二进制的与、或什么的。(例: %8 就可以写成 &7,/2就可以写成>>1 之类的吧。。)
    4.
    inline和register ? 这种东西不知道靠不靠谱。。
    5.
    避免多次操作。
    比如线段树中要多次用到pos*2、pos*2+1这种东西。怎么办呢? 我们可以开个变量保存它。用的时候就没有必要再重复计算了。
    6.
    最重要的一点吧。。 知道正解最好。。。。。。。。

    附正解的二分算法:
    二分前多少个订单可以满足,然后判断。
    建立一个数组是a[i]=第i天需要的教室数-第i-1天需要的教室数
    这样第i天需要的教室数就是a[]的前缀和。
    每个订单a~b增加c就是a[a]+=c,a[b+1]-=c,把订单信息确定后之后扫一遍进行判断。

    // by SiriusRen
    #include <cstdio>
    #include <cstring>
    #define N 1000005
    using namespace std;
    int l=1,r,n,answer,mid,m,a[N],b[N],d[N],s[N],t[N];
    bool solve(){
        int ans=0;memset(b,0,sizeof(b));
        for(int i=1;i<=mid;i++)b[s[i]]+=d[i],b[t[i]+1]-=d[i];
        for(int i=1;i<=n;i++){
            ans+=b[i];
            if(a[i]<ans)return false;
        }
        return true;
    }
    int main(){
        scanf("%d%d",&n,&m);r=m;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=r;i++)scanf("%d%d%d",&d[i],&s[i],&t[i]);
        while(l<=r){
            mid=(l+r)/2;
            if(solve())answer=mid,l=mid+1;
            else r=mid-1;
        }
        if(answer!=m)printf("-1
    %d
    ",answer+1);
        else puts("0");
    }

    写完线段树还是觉得二分so easy……

  • 相关阅读:
    springboot之热部署
    在动态sql的使用where时,if标签判断中,如果实体类中的某一个属性是String类型,那么就可以这样来判断连接语句:
    对集合进行判空的操作
    配置logback日志管理的时候
    SpringBoot序列化时间类型的问题
    Cannot determine embedded database driver class for database type NONE
    idea的基础设置
    使用navicat创建数据库
    LESS
    数据库链接池--简单的理解
  • 原文地址:https://www.cnblogs.com/SiriusRen/p/6532426.html
Copyright © 2011-2022 走看看