[BZOJ 1135][POI2009]Lyz

[BZOJ 1135][POI2009]Lyz

题意

初始时滑冰俱乐部有 \(1\)\(n\) 号的溜冰鞋各 \(k\) 双。已知 \(x\) 号脚的人可以穿 \(x\)\(x+d\) 的溜冰鞋。

\(m\) 次操作,每次包含两个数 \(r_i,x_i\) 代表来了 \(x_i\) 个 $r_i \(号脚的人。\)x_i$ 为负,则代表走了这么多人。 对于每次操作,输出溜冰鞋是否足够。足够输出 TAK, 否则输出 NIE.

\(n\le 2\times 10^5,m\le5\times 10^5,k\le1\times 10^9,d\in[0,n],1\le r_i\le n-d,|x_i|\le1\times 10^9\)

题解

被某Robbery少许加强后丢到了胡策里...

由霍尔定理, 二分图存在完美匹配当且仅当一侧的任意一个子集中的点连接的另一侧的点的数量都不小于这个子集的大小. 虽然看上去要枚举子集但是实际上我们发现不连续的几段更有可能满足霍尔定理的要求, 所以只要连续区间都满足霍尔定理的要求那么所有子集就都满足了. 感性证明如下:

假设选中了一个不连续的子集, 那么显然可以在不改变另外一侧的邻接情况下在当前子集中添加新的点. 如果不能添加新的点的话可以把子集拆成若干部分, 每个部分是一个连续段. 显然拆开后或者添加新点后更可能会破坏霍尔定理的要求.

也就是说如果设 \(i\) 号脚的人共有 \(s_i\) 个, 那么溜冰鞋不足当且仅当存在任意一个区间 \([l,r]\) 满足下式:
\[ \sum_{i=l}^rs_i>k(r-l+d+1) \]
那么我们拆开移项就可以得到:
\[ \begin{aligned} \sum_{i=l}^rs_i-k(r-l+1)&>kd \\ \sum_{i=1}^r(s_i-k)&>kd \end{aligned} \]
于是就变成了一个支持单点加法的动态区间最大子段和问题. 线段树动态DP经典操作.

加强版里不保证 \(r\le n-d\), 需要继续考虑 \(r>n-d\) 的情况. 此时溜冰鞋不足的充要条件相当于:
\[ \sum_{i=l}^rs_i>k(n-l+1) \]
显然当 \(r=n\) 的时候左侧取到最大值, 我们只计算 \(r=n\) 时是否满足条件即可. 此时相当于:
\[ \sum_{i=l}^ns_i>k(n-l+1) \]
\(S\)\(\langle s_i\rangle\) 的前缀和, 那么我们可以发现上式等价于:
\[ \begin{aligned} S_n-S_{l-1}&>kn-k(l-1) \\ k(l-1)-S_{l-1}&>kn-S_n \end{aligned} \]
左侧的最大值显然也可以用线段树维护出来.

参考代码

#include <bits/stdc++.h>

const int MAXN=1e5+10;
typedef long long intEx;

struct Node{
    struct Data{
        intEx sum;
        intEx lmax;
        intEx maxs;
        intEx rmax;
        Data(){}
        Data(intEx val){
            this->sum=val;
            this->lmax=this->rmax=this->maxs=std::max(this->sum,0ll);
        }
        Data friend operator+(const Data& a,const Data& b){
            Data ans;
            ans.sum=a.sum+b.sum;
            ans.lmax=std::max(a.lmax,a.sum+b.lmax);
            ans.rmax=std::max(a.rmax+b.sum,b.rmax);
            ans.maxs=std::max(a.rmax+b.lmax,std::max(a.maxs,b.maxs));
            return ans;
        }
    };
    int l;
    int r;
    Data val;
    Node* lch;
    Node* rch;
    Node(int,int);
    void Maintain();
    void Add(int,int);
};

int n;
int q;
int k;
int d;

int main(){
    scanf("%d%d%d%d",&n,&q,&k,&d);
    Node* N=new Node(1,n);
    for(int i=0;i<q;i++){
        int p,x;
        scanf("%d%d",&p,&x);
        N->Add(p,x);
        if(N->val.maxs>1ll*k*d)
            puts("NIE");
        else
            puts("TAK");
    }
    return 0;
}

Node::Node(int l,int r):l(l),r(r){
    if(l==r)
        this->val=Data(-k);
    else{
        int mid=(l+r)>>1;
        this->lch=new Node(l,mid);
        this->rch=new Node(mid+1,r);
        this->Maintain();
    }
}

void Node::Add(int x,int d){
    if(this->l==this->r)
        this->val=Data(this->val.sum+d);
    else{
        if(x<=this->lch->r)
            this->lch->Add(x,d);
        else
            this->rch->Add(x,d);
        this->Maintain();
    }
}

inline void Node::Maintain(){
    this->val=this->lch->val+this->rch->val;
}

加强版写的比较蠢...写了一个维护最大子段和的和一个区间加法区间最值的线段树...

#include <bits/stdc++.h>

const int MAXN=1e5+10;
typedef long long intEx;

struct Node{
    struct Data{
        intEx sum;
        intEx lmax;
        intEx maxs;
        intEx rmax;
        Data(){}
        Data(intEx val){
            this->sum=val;
            this->lmax=this->rmax=this->maxs=std::max(this->sum,0ll);
        }
        Data friend operator+(const Data& a,const Data& b){
            Data ans;
            ans.sum=a.sum+b.sum;
            ans.lmax=std::max(a.lmax,a.sum+b.lmax);
            ans.rmax=std::max(a.rmax+b.sum,b.rmax);
            ans.maxs=std::max(a.rmax+b.lmax,std::max(a.maxs,b.maxs));
            return ans;
        }
    };
    int l;
    int r;
    Data val;
    Node* lch;
    Node* rch;
    Node(int,int);
    void Maintain();
    void Add(int,int);
};

struct NodeX{
    int l;
    int r;
    intEx add;
    intEx max;
    NodeX* lch;
    NodeX* rch;
    NodeX(int,int);
    void PushDown();
    void Maintain();
    void Add(int,int,int);
    void Add(const intEx&);
};

int n;
int q;
int k;
int d;

int main(){
    scanf("%d%d%d%d",&n,&q,&k,&d);
    Node* N=new Node(1,n);
    NodeX* K=new NodeX(0,n-1);
    for(int i=0;i<q;i++){
        int p,x;
        scanf("%d%d",&p,&x);
        N->Add(p,x);
        if(p!=n)
            K->Add(p,n-1,-x);
//      printf("%lld %lld\n",N->val.maxs,K->max);
        if(N->val.maxs>1ll*k*d||K->max>-N->val.sum)
            puts("No");
        else
            puts("Yes");
    }
    return 0;
}

NodeX::NodeX(int l,int r):l(l),r(r),add(0){
    if(l==r)
        this->max=1ll*l*k;
    else{
        int mid=(l+r)>>1;
        this->lch=new NodeX(l,mid);
        this->rch=new NodeX(mid+1,r);
        this->Maintain();
    }
}

void NodeX::Add(const intEx& d){
    this->add+=d;
    this->max+=d;
}

void NodeX::Add(int l,int r,int d){
    if(l<=this->l&&this->r<=r)
        this->Add(d);
    else{
        this->PushDown();
        if(l<=this->lch->r)
            this->lch->Add(l,r,d);
        if(this->rch->l<=r)
            this->rch->Add(l,r,d);
        this->Maintain();
    }
}

void NodeX::PushDown(){
    if(this->add){
        this->lch->Add(this->add);
        this->rch->Add(this->add);
        this->add=0;
    }
}

void NodeX::Maintain(){
    this->max=std::max(this->lch->max,this->rch->max);
}

Node::Node(int l,int r):l(l),r(r){
    if(l==r)
        this->val=Data(-k);
    else{
        int mid=(l+r)>>1;
        this->lch=new Node(l,mid);
        this->rch=new Node(mid+1,r);
        this->Maintain();
    }
}

void Node::Add(int x,int d){
    if(this->l==this->r)
        this->val=Data(this->val.sum+d);
    else{
        if(x<=this->lch->r)
            this->lch->Add(x,d);
        else
            this->rch->Add(x,d);
        this->Maintain();
    }
}

inline void Node::Maintain(){
    this->val=this->lch->val+this->rch->val;
}

猜你喜欢

转载自www.cnblogs.com/rvalue/p/10776519.html