【题解】 AtCoder ARC 076 F - Exhausted? (霍尔定理+线段树)

题面

题目大意:

给你\(m\)张椅子,排成一行,告诉你\(n\)个人,每个人可以坐的座位为\([1,l]\bigcup[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]\bigcup[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\),那么\(r<=r_i,r>=0\)的人数都可以加一(因为我们此时\(l\)是固定的,保证了这个人\(l\)在范围内,如果枚举的\(r\geq r_i\),那么这个人数是要算进去的)。
  • 如果上面你看懂了,那就很好办了,我们会发现对于枚举的\(r\),式子中的人数\(-m-1+r\)是一样的,人数我们是在推进\(l\)同时加入线段树,\(-m-1+r\)我们就可以提前加入线段树(建树)
  • 对于一种特殊的情况,比如说:\(l_i=1,r_i=1\),那么他能放的范围为所有,所以我们要判断\(m-n\)为答案的情况
  • 对于普通枚举\(l\),我们没必要考虑全部集合情况,所以我们就只用在线段树\(l+2\rightarrow m+1\)找出最大值减去\(l\),得到的答案与\(ans\)取\(max\)就\(ok\)啦

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

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

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

Attention:

  1. 线段树是从\(0\rightarrow 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\n",ans);
  return 0;
}

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

原文地址:https://www.cnblogs.com/Ning-Mew/p/9320708.html

时间: 2024-10-29 03:41:40

【题解】 AtCoder ARC 076 F - Exhausted? (霍尔定理+线段树)的相关文章

【AtCoder ARC076】F Exhausted? 霍尔定理+线段树

题意 N个人抢M个椅子,M个椅子排成一排 ,第i个人只能坐[1,Li]∪[Ri,M],问最多能坐多少人 $i$人连边向可以坐的椅子构成二分图,题意即是求二分图最大完美匹配,由霍尔定理,答案为$max(|X|-\omega(X))$,$X$为人的集合,$\omega(X)$可以表示为$[1,l] \cup[r,M]$,所以可以枚举$\omega(X)$也就是$(l,r)$,求出最大的$|X|$,也就是满足$L_i\le l \land r \le R_i$的$i$的数量,也就是平面上以$(l,r)

Atcoder Regular Contest 076 F - Exhausted?题解

题目链接:F - Exhausted? 题目大意:有m个椅子在数轴上排列,第i张椅子(1≤i≤m)的坐标为i. 高桥君和他的朋友一共有n个人.高桥君他们因为玩了太久的游戏,大家的腰和背都很痛,所以他们很有必要坐在椅子上休息一下.高桥君他们每个人坐的椅子的坐标都很讲究,第i个人想坐在坐标在li以下(包括li)的椅子上,或者坐在坐标在ri以上(包括ri)的椅子上.当然,一个的椅子只能坐一个人. 可这样计算下去,可能会让他们不能都坐在椅子上休息.青木君关心高桥君他们的健康,尽可能多地增加椅子,让高桥君

Educational Codeforces Round 23 F. MEX Queries(线段树)

题目链接:Educational Codeforces Round 23 F. MEX Queries 题意: 一共有n个操作. 1.  将[l,r]区间的数标记为1. 2.  将[l,r]区间的数标记为0. 3.  将[l,r]区间取反. 对每个操作,输出标记为0的最小正整数. 题解: hash后,用线段树xjb标记一下就行了. 1 #include<bits/stdc++.h> 2 #define ls l,m,rt<<1 3 #define rs m+1,r,rt<&l

Codeforces Round #FF (Div. 2) E. DZY Loves Fibonacci Numbers(斐波那契的定理+线段树)

/* 充分利用了菲波那切数列的两条定理: ①定义F[1] = a, F[2] = b, F[n] = F[n - 1] + F[n - 2](n≥3). 有F[n] = b * fib[n - 1] + a * fib[n - 2](n≥3),其中fib[i]为斐波那契数列的第 i 项. ②定义F[1] = a, F[2] = b, F[n] = F[n - 1] + F[n - 2](n≥3). 有F[1] + F[2] + -- + F[n] = F[n + 2] - b 这题还有一个事实,

[CTSC2017]游戏(Bayes定理,线段树)

传送门:http://uoj.ac/problem/299 题目良心给了Bayes定理,但对于我这种数学渣来说并没有什么用. 先大概讲下相关数学内容: 1.定义:$P(X)$ 表示事件$X$发生的概率,$E(X)$表示随机变量$X$的期望值,$P(A|B)$表示已知$B$发生,$A$发生的概率,$P(AB)$表示$A$和$B$同时发生的概率. 2.条件概率公式: $\begin{aligned}P(A|B)=\frac{P(AB)}{P(B)}\end{aligned}$. 由$P(B)P(A|

The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest - F.Sequence(打表+线段树)

题意:给你一个长度为$n$的数组,定义函数$f(l,r)=a_{l} \oplus a_{l+1} \oplus...\oplus a_{r}$,$F(l,r)=f(l,l)\oplus f(l,l+1)\oplus ...\oplus f(l,r)\oplus f(l+1,l+1)\oplus ...f(l+1,r)\oplus ...\oplus f(r,r)$,有两种操作,第一种将数组中某个元素$a[x]$变为$y$,第二种计算$F(l,r)$的值. 思路:打表后发现只有当$l$和$r$同

【BZOJ2138】stone Hall定理+线段树

[BZOJ2138]stone Description 话说Nan在海边等人,预计还要等上M分钟.为了打发时间,他玩起了石子.Nan搬来了N堆石子,编号为1到N,每堆包含Ai颗石子.每1分钟,Nan会在编号在[Li,Ri]之间的石堆中挑出任意Ki颗扔向大海(好疼的玩法),如果[Li,Ri]剩下石子不够Ki颗,则取尽量地多.为了保留扔石子的新鲜感,Nan保证任意两个区间[Li,Ri]和[Lj,Rj],不会存在Li<=Lj&Rj<=Ri的情况,即任意两段区间不存在包含关系.可是,如果选择不

【题解】Luogu P5342 [TJOI2019]甲苯先生的线段树

原题传送门 挺有趣的一道题 \(c=1\),暴力求出点权和n即可 \(c=2\),先像\(c=1\)一样暴力求出点权和n,考虑有多少路径点权和也为n 考虑设x为路径的转折点,\(L\)为\(x\)向左儿子走的长度,\(R\)为\(x\)向右儿子走的长度.易知当\(L,R\)确定时,有唯一的\(x\)对应 以\(x\)为转折点,\(L,R\)为向左/右儿子走的距离,这时点权和至少为\(Min=(2^{L+1}+2^{R+1}-3)x+2^R-1\) 此时x的取值一定珂以求出.考虑一下如何产生剩下\

【Educational Codeforces Round 37】F. SUM and REPLACE 线段树+线性筛

题意 给定序列$a_n$,每次将$[L,R]$区间内的数$a_i$替换为$d(a_i)$,或者询问区间和 这题和区间开方有相同的操作 对于$a_i \in (1,10^6)$,$10$次$d(a_i)$以内肯定可以最终化为$1$或者$2$,所以线段树记录区间最大值和区间和,$Max\le2$就返回,单点暴力更新,最后线性筛预处理出$d$ 时间复杂度$O(m\log n)$ 代码 #include <bits/stdc++.h> using namespace std; typedef long