ARC 076 F Exhausted? - 网络流 - 霍尔定理 - 线段树

首先可以前缀和优化网络流建图并得到TLE的好成绩。
可以直接用霍尔定理(准确的说是其推广)转化,但是突然忘了Hull定理是啥了。
网上很多贪心都是错的,最经典的例如按照左端点排序相同就看右端点的做法可以被n=m=4, (1,3), (2,4), (2,5), (0,4)卡掉。
然后还是回来考虑网络流,开始思考这个特殊的网络流有什么转化方式可以直接做,人脑了1h后弃疗。
紧接着突然意识到最大流等于最小割!
重新看这个问题,考虑网络流的建图,令x表示左边跟n有关的点,y表示右边跟m有关的点,那么S连x流量1,y连T流量1,其余流量正无穷,表示不能割。
然后考虑最终割集长什么样,显然是左边的一些东西和右边的一个前缀和一个后缀(或者全选,这个就是m,这个特判一下即可)。
枚举右边选的前缀是[1,l],选的后缀是[r,m],(可以有l=0或者r=m+1),考虑首先这l+m-r+1条边要被割去,其次满足 Lx>l L x > l 或者 Rx<r R x < r 的(S,x,1)要被割去。
直接做是平方级别的,考虑 Lx>l L x > l 或者 Rx<r R x < r 等价于, Lx>l L x > l 或者( Lxl L x ≤ l 并iqe Rx<r R x < r ),那么从小到大枚举l,就是l+m+1+(L_x>l的x的数量)可以直接维护,剩下的是在固定l的时候求最小的(L_x<=l并且R_x< r)-r,这个显然类似扫描线一下就好了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define gc getchar()
#define mp make_pair
#define fir first
#define sec second
#define N 200010
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct segment{
    int l,r,mn,pt;
    segment *ch[2];
}*rt;pii a[N];
inline int push_up(segment* &rt) { return rt->mn=min(rt->ch[0]->mn,rt->ch[1]->mn); }
inline int update_tags(segment* &rt,int v) { return rt->mn+=v,rt->pt+=v; }
inline int push_down(segment* &rt)
{   return update_tags(rt->ch[0],rt->pt),update_tags(rt->ch[1],rt->pt),rt->pt=0;    }
int build(segment* &rt,int l,int r,int x)
{
    rt=new segment,rt->l=l,rt->r=r,rt->pt=0;
    if(l==r) return rt->mn=x-l;int mid=(l+r)>>1;
    build(rt->ch[0],l,mid,x),build(rt->ch[1],mid+1,r,x);
    return push_up(rt);
}
int update(segment* &rt,int s,int t,int v)
{
    if(s>t) return 0;
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(s<=l&&r<=t) return update_tags(rt,v);
    if(rt->pt) push_down(rt);
    if(s<=mid) update(rt->ch[0],s,t,v);
    if(mid<t) update(rt->ch[1],s,t,v);
    return push_up(rt);
}
int main()
{
    int n=inn(),m=inn(),Ans=min(n,m);
    for(int i=1,l,r;i<=n;i++)
        l=inn(),r=inn(),a[i]=mp(l,r);
    sort(a+1,a+n+1),build(rt,1,m+1,m+1);
    for(int l=0,c=0;l<=m;l++)
    {
        while(c<n&&a[c+1].fir<=l)
            c++,update(rt,a[c].sec+1,m+1,1);
        Ans=min(Ans,n-c+l+rt->mn);
    }
    return !printf("%d\n",n-Ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值