• 【题解】 AtCoder ARC 076 F


    题面

    题目大意:

    给你(m)张椅子,排成一行,告诉你(n)个人,每个人可以坐的座位为([1,l]igcup[r,m]),为了让所有人坐下,问至少还要加多少张椅子。

    Solution:

    • 为什么加椅子?我们可以在最左边或最右边一直加直到人人都有座位。
    • 首先这道题目抽象成二分图很简单,然后我们可以只要求解出人与座位的最大匹配是多少,总人数减去即可,但跑二分图最大匹配显然会超时,我们就可以往霍尔定理方面想。
    • 然后你还需要知道一个霍尔定理推论:假设某个人的集合为(X),这个集合所对应的椅子的集合为(Y),如果(|X|leq|Y|),则具有完美匹配,如果(|X|geq|Y|),则(X)至少要删去(|X|-|Y|)个元素,才能有完备匹配,我们定义(Gamma(X)=|X|-|Y|)

    在这题里,这个就是至少需要添加的椅子数目,所以我们要找出最大的(Gamma(X))

    • 接下来我们就来分析怎么找出最大的(Gamma(X)),因为(X)不具有任何性质,不好下手,我们考虑(Y)有啥特点,他一定是([1,l]igcup[r,m]),然后我们可以通过这个(Y)确定(|X|),所以会有一下做法

    法一:暴力枚举

    • 暴力枚举(l,r),椅子的个数为(l+m-r+1),通过上面的定理处理出答案,找出(Gamma)最大值。时间复杂度:(O(n^2))貌似会更高到三方

    法二:与其说是法二不如说是法一优化

    • 我们考虑上面的算法哪一些地方是冗余的。假设我们枚举出(l),根据法一,我们会枚举出所有的(r),显然不用枚举所有的,实际上只有找出这个(l)对应后面的(r)算出来的最大值,如何快速查询出最大值,我们考虑使用线段树。
    • 具体如何优化这个问题:计算式子是人数(-(l+m-r+1))
    • 因为固定了(l),我们可以确定现在最多有多少人,总人数与(l')有关((l'<l))所以我们把(l)从小到大排序,把人数不断往线段树里面丢。
    • 具体多少人根据(r)决定,某一个人要加入计算的人数内,必须是他所允许的所有椅子范围都在讨论范围内,比如这个(l=l_i),那么(0leq rleq r_i)的人数都可以加一(因为我们此时(l)是固定的,保证了这个人(l)在范围内,如果枚举的(r_j leq r_i),那么这个人数是要在算(l ightarrow r_j)时算进去的)。
    • 如果上面你看懂了,那就很好办了,我们会发现对于枚举的(r),式子中的人数(-m-1+r)是一样的,人数我们是在推进(l)同时加入线段树,(-m-1+r)我们就可以提前加入线段树(建树)
    • 对于一种特殊的情况,比如说:(l_i=1,r_i=1),那么他能放的范围为所有,所以我们要判断(m-n)为答案的情况
    • 对于普通枚举(l),我们没必要考虑全部集合情况,所以我们就只用在线段树(l+2 ightarrow m+1)找出最大值减去(l),得到的答案与(ans)(max)(ok)

    • 下面我们来将乱搞做法,我们把人按(l)排序,枚举到人后把他可以座椅子的范围用线段树标记,然后算出总可以坐的椅子数与人数相比较,求出每次(ans)(max),一开始错两个点,然后排了两个序,算两次就只错一个点了。这个方法显然是不对哒!!因为显然有一些人的子集没有枚举到。

    这道题好理解吧(毛线,我起码看了一个下午才看懂,去吃饭的路上突然懂了23333)

    对了贪心也可以过这道题,只不过做法没这么优美罢了

    Attention:

    1. 线段树是从(0 ightarrow m+1),因为人的(l,r)包含(0,m+1)
    2. 特殊情况,(|X|=n)
    3. 枚举出(l)后,找最大值一定是从([l+1,m+1])中找

    First, 你枚举的(r)(>l)
    Second,(|X|=n)已经被考虑(没那个必要了,其实你从(l+1)开始也不影响)

    Code:

    //It is coded by ning_mew on 7.16
    #include<bits/stdc++.h>
    #define ls(x) (x*2)
    #define rs(x) (x*2+1)
    using namespace std;
    
    const int maxn=2e5+7,inf=1e9+7;
    
    int n,m,ans=0;
    struct Opt{int l,r;}p[maxn];
    struct Node{int lazy,maxx;}node[maxn*4];
    
    bool cmp(const Opt &A,const Opt &B){return A.l<B.l;}
    void pushdown(int num,int nl,int nr){
      if(!node[num].lazy)return;
      int lz=node[num].lazy; node[num].lazy=0;
      node[ls(num)].maxx+=lz;node[ls(num)].lazy+=lz;
      node[rs(num)].maxx+=lz;node[rs(num)].lazy+=lz;	
    }
    void update(int num){node[num].maxx=max(node[ls(num)].maxx,node[rs(num)].maxx);}
    void build(int num,int nl,int nr){
      node[num].lazy=0; if(nl==nr){node[num].maxx=nr-m-1;return;}
      int mid=(nl+nr)/2;
      build(ls(num),nl,mid);build(rs(num),mid+1,nr);
      update(num);
    }
    void add(int num,int nl,int nr,int ql,int qr,int ad){
      if(ql<=nl&&nr<=qr){node[num].maxx+=ad;node[num].lazy+=ad;return;}
      if(nr<ql||qr<nl)return;
      int mid=(nl+nr)/2;pushdown(num,nl,nr);
      add(ls(num),nl,mid,ql,qr,ad);add(rs(num),mid+1,nr,ql,qr,ad);
      update(num);return;
    }
    int quary(int num,int nl,int nr,int ql,int qr){
      if(ql<=nl&&nr<=qr)return node[num].maxx;
      if(nr<ql||qr<nl)return -inf;
      int mid=(nl+nr)/2;pushdown(num,nl,nr);
      return max(quary(ls(num),nl,mid,ql,qr),quary(rs(num),mid+1,nr,ql,qr));
    }
    int main(){
      scanf("%d%d",&n,&m);
      for(int i=1;i<=n;i++)scanf("%d%d",&p[i].l,&p[i].r);
      sort(p+1,p+n+1,cmp);p[n+1].l=inf;
      build(1,0,m+1);
      for(int i=1;i<=n;i++){
        add(1,0,m+1,0,p[i].r,1);
        if(p[i].l!=p[i+1].l&&p[i].l<=m-1)ans=max(ans,quary(1,0,m+1,p[i].l+2,m+1)-p[i].l);
      }ans=max(ans,n-m);printf("%d
    ",ans);
      return 0;
    }
    

    博主蒟蒻,随意转载。但必须附上原文链接:http://www.cnblogs.com/Ning-Mew/,否则你会场场比赛暴0!!!

  • 相关阅读:
    Go语言 0 前言
    SVM支持向量机的基本原理
    支持向量机通俗介绍
    线程句柄和线程ID的区别
    分布式事务一致性,事务补偿实战
    ResultSet is from UPDATE. No Data.
    linux 批量kill进程
    mysql数据表拷贝
    Maven assembly 打包
    JS 删除对象属性
  • 原文地址:https://www.cnblogs.com/Ning-Mew/p/9320708.html
Copyright © 2020-2023  润新知