luogu P3071 [USACO13JAN]Seating G 题解

题目大意

详见题目翻译。

解题思路

L操作也就是区间推平操作,意味着这题可以用珂朵莉树做。

当前块的值为 0 0 0,代表当前区间没有人坐。否则,当前区间有人坐。

A操作就暴力遍历整个集合,寻找一个长度大于 l l l 的块,用 r r r s u m sum sum 分别表示块的最左边和当前的块长。

如果 s u m ≥ l sum\geq l suml,就直接返回 r r r.

如果当前块的值为 0 0 0 s u m sum sum 加上当前块长,如果 r = 0 r=0 r=0,就将当前块的左边赋值给 r r r.

如果当前块的值为 1 1 1,就将 r r r s u m sum sum 重置为 0 0 0.

L操作就是珂朵莉树基本操作–区间推平。

代码实现

#include
using namespace std;
int n,m,ans=0;
char s[10+10];
struct node
{
    int l,r;
    mutable int v;
    node(int L,int R=-1,int V=0)
    {
        l=L,r=R,v=V;
    }
    bool operator <(const node &a) const
    {
        return l<a.l;
    }
};
set<node> a;
#define at set<node>::iterator
at split(int pos)
{
    at it=a.lower_bound(pos);
    if(it!=a.end()&&it->l==pos)
        return it;
    it--;
    int l=it->l;
    int r=it->r;
    int v=it->v;
    a.erase(it);
    a.insert(node(l,pos-1,v));
    return a.insert(node(pos,r,v)).first;
}
void emerge(int l,int r,int v)
{
    at itr=split(r+1);
    at itl=split(l);
    a.erase(itl,itr);
    a.insert(node(l,r,v));
    return ;
}
int ask(int l)
{
    int r=0,sum=0;
    for(at it=a.begin();it!=a.end();++it)
    {
        if(sum>=l)
            return r;
        if(it->v==0)
        {
            if(r==0)
                r=it->l;
            sum+=it->r-it->l+1;
        }
        else    
            r=0,sum=0;
    }
    return -1;
}
int main()
{
    scanf("%d %d",&n,&m);
    a.insert(node(1,n,0));
    a.insert(node(n+1,n+10,1));
    for(int i=1;i<=m;++i)
    {
        int l,r;
        scanf("%s",s);
        if(s[0]=='A')
        {
            scanf("%d",&l);
            if(l==5)
                l=5; 
            int r=ask(l);  
            if(r!=-1)
                emerge(r,r+l-1,1);
            else    
                ans++;   
        }
        else
        {
            scanf("%d %d",&l,&r);
            emerge(l,r,0);
        }      
    }
    cout<<ans;
    return 0;
}

你可能感兴趣的:(题解,算法,c++)