[bzoj1135][POI2009]Lyz——Hall定理+线段树

题目大意:

初始时滑冰俱乐部有1到n号的溜冰鞋各k双。已知x号脚的人可以穿x到x+d的溜冰鞋。 有m次操作,每次包含两个数ri,xi代表来了xi个ri号脚的人。xi为负,则代表走了这么多人。 对于每次操作,输出溜冰鞋是否足够。

思路:

感觉挺不错的一道题,果然POI的题目质量确实不错。
显然这是人和鞋子的二分图匹配,然后我们要求人这一边是否有完备匹配,那就用Hall定理了。
Hall定理是指数级别的枚举,由于这一题的特殊性,人如果是连续的,鞋子也必定连续,如果两段不连续的人他们的鞋子连在了一起,那么我们不妨把中间的部分选满。如果有一个集合不满足条件,那么肯定是一段连续的人的集合不满足条件,所以就只要处理连续的就好了。
设尺寸 i i 的人数为ai,那么我们就要判断

i=lrai(rl+1+d)k ∑ i = l r a i ≤ ( r − l + 1 + d ) ∗ k
是否成立就好了,然后一个很强的操作就是式子化为了
i=lr(aik)dk ∑ i = l r ( a i − k ) ≤ d ∗ k

然后就只要用线段树来维护区间的最大连续子段和就好了。

/*=================================
 * Author : ylsoi
 * Algorithm : Hall & Segment_Tree
 * Problem : bzoj1135
 * Time : 2018.7.12
 * ===============================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj1135.in","r",stdin);
    freopen("bzoj1135.out","w",stdout);
}

template<typename T>T _max(T _,T __){return _>__ ? _ : __;}
const int maxn=2e5+10;
const ll inf=LLONG_MAX;
int n,m,k,d;

struct Segment_Tree{
#define mid ((l+r)>>1)
#define lc rt<<1
#define rc rt<<1|1
#define lson lc,l,mid
#define rson rc,mid+1,r
    ll sum[maxn<<2],Maxl[maxn<<2],Maxr[maxn<<2],Max[maxn<<2];
    void pushup(int rt){
        sum[rt]=sum[lc]+sum[rc];
        Maxl[rt]=_max(Maxl[lc],sum[lc]+Maxl[rc]);
        Maxr[rt]=_max(Maxr[rc],sum[rc]+Maxr[lc]);
        Max[rt]=_max(Maxr[lc]+Maxl[rc],_max(Max[lc],Max[rc]));
    }
    void build(int rt,int l,int r){
        if(l==r)sum[rt]=Max[rt]=Maxl[rt]=Maxr[rt]=-k;
        else build(lson), build(rson), pushup(rt);
    }
    void update(int rt,int l,int r,int pos,int x){
        if(l==r){
            sum[rt]+=x;
            Max[rt]=Maxl[rt]=Maxr[rt]=sum[rt];
        }
        else{
            if(pos<=mid)update(lson,pos,x);
            else update(rson,pos,x);
            pushup(rt);
        }
    }
}T;

int main(){
    File();
    scanf("%d%d%d%d",&n,&m,&k,&d);
    T.build(1,1,n);
    REP(i,1,m){
        int u,v;
        scanf("%d%d",&u,&v);
        T.update(1,1,n,u,v);
        if(T.Max[1]<=(ll)k*d)puts("TAK");
        else puts("NIE");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值