[BZOJ 1135][POI2009]Lyz

[BZOJ 1135][POI2009]Lyz

題意

初始時滑冰俱樂部有 \(1\)\(n\) 號的溜冰鞋各 \(k\) 雙。已知 \(x\) 號腳的人能夠穿 \(x\)\(x+d\) 的溜冰鞋。php

\(m\) 次操做,每次包含兩個數 \(r_i,x_i\) 表明來了 \(x_i\)\(r_i\) 號腳的人。\(x_i\) 爲負,則表明走了這麼多人。 對於每次操做,輸出溜冰鞋是否足夠。足夠輸出 TAK, 不然輸出 NIE.c++

\(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\)this

題解

被某Robbery少量增強後丟到了胡策裏...spa

由霍爾定理, 二分圖存在完美匹配當且僅當一側的任意一個子集中的點鏈接的另外一側的點的數量都不小於這個子集的大小. 雖然看上去要枚舉子集可是實際上咱們發現不連續的幾段更有可能知足霍爾定理的要求, 因此只要連續區間都知足霍爾定理的要求那麼全部子集就都知足了. 感性證實以下:code

假設選中了一個不連續的子集, 那麼顯然能夠在不改變另一側的鄰接狀況下在當前子集中添加新的點. 若是不能添加新的點的話能夠把子集拆成若干部分, 每一個部分是一個連續段. 顯然拆開後或者添加新點後更可能會破壞霍爾定理的要求.blog

也就是說若是設 \(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經典操做.get

增強版裏不保證 \(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} \]
左側的最大值顯然也能夠用線段樹維護出來.it

參考代碼

#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;
}

增強版寫的比較蠢...寫了一個維護最大子段和的和一個區間加法區間最值的線段樹...class

#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;
}

相關文章
相關標籤/搜索