DataStructure

HDU-1540-Tunnel Warfare(线段树的区间合并

2019-08-01  本文已影响0人  雨落八千里

Tunnel Warfare

思路:

  • 这明明是单点查询,怎么说是区间合并呢?试问一下,题目要求是不是输出当前点连续的长度呢!!!

  • 单点查询如何转换成区间查询?这就是题目的巧妙之处,查询的时候,在整个区间缩小为一个点的过程中,判断目标点是否在当前区间的连续区间内,如果在,我们再查询该连续区间的端点。(相当于感染)这样就可以查找到目标点所在的连续区间。

  • 说的简单一点就是当前的点属于哪个较长的区间,这就不成区间查询了

  • 首先是建树,在结构体里定义ls记录该区间左端点的最大连续个数,rs记录区间右端点开始的最大连续个数,ms表示该区间最大的连续个数。

#include<bits/stdc++.h>
using namespace std;
const int M=55000;
stack<int>s;
struct node
{
    int l,r;
    int ls,rs,ms;//ls从l开始左边最大连续,rs从r开始右边最大连续,整个l-s最长连续
} tree[M<<2];
void pushup(int root)
{
    tree[root].ls=tree[root<<1].ls;//左连续
    tree[root].rs=tree[root<<1|1].rs;//右连续
    tree[root].ms=max(max(tree[root<<1].ms,tree[root<<1|1].ms),tree[root<<1].rs+tree[root<<1|1].ls);
    //父亲区间内的最大区间必定是,左子树最大区间,右子树最大区间,左右子树连续区间合并的中间区间,三者中最大的区间值
    if(tree[root<<1].ls==tree[root<<1].r-tree[root<<1].l+1)//说明左子树全部连续
    {
        tree[root].ls+=tree[root<<1|1].ls;//左子树区间满了的话,父亲左区间要加上右子树的左连续
    }
    if(tree[root<<1|1].rs==tree[root<<1|1].r-tree[root<<1|1].l+1)//说明右子树全部连续
    {
        tree[root].rs+=tree[root<<1].rs;//右子树区间满了的话,父亲右区间就要加上左子树的右连续
    }
}
void build(int l,int r,int root)
{
    tree[root].l=l;
    tree[root].r=r;
    tree[root].ls=tree[root].rs=tree[root].ms=r-l+1;
    if(l!=r)
    {
        int mid=(l+r)>>1;
        build(l,mid,root<<1);
        build(mid+1,r,root<<1|1);
    }
}
// void print(int l,int r,int root)
// {
//     if(l==r)
//     {
//         printf("%d l %d\n",l,tree[root].ms);
//         return ;
//     }
//     int mid=(l+r)>>1;
//     print(l,mid,root<<1);
//     print(mid+1,r,root<<1|1);
// }
void update(int root,int pos,int flag)
{
    if(tree[root].l==tree[root].r)
    {
        if(flag==1)
        {
            tree[root].ls=tree[root].rs=tree[root].ms=1;//修复
        }
        else
        {
            tree[root].ls=tree[root].rs=tree[root].ms=0;//摧毁
        }
        return ;
    }
    int mid=(tree[root].l+tree[root].r)>>1;
    if(pos<=mid)
    {
        update(root<<1,pos,flag);
    }
    else
    {
        update(root<<1|1,pos,flag);
    }
    pushup(root);
}
int query(int root,int pos)
{
    if(tree[root].l==tree[root].r||tree[root].ms==0||tree[root].ms==tree[root].r-tree[root].l+1)
    {
        return tree[root].ms;//到了叶子节点或者该访问区间为空或者已满都不必要往下走了
    }
    int mid=(tree[root].l+tree[root].r)>>1;
    if(pos<=mid)
    {
        if(pos>=tree[root<<1].r-tree[root<<1].rs+1)//因为pos<=mid,看左子树,tree[root<<1].r-tree[root<<1].rs+1代表左子树右连续区间的左边界值,如果pos在左子树的右连续内,则要看右子树的左连续有多长并返回
        {
            return query(root<<1,pos)+query(root<<1|1,mid+1);
        }
        else
        {
            return query(root<<1,pos);//如果不在左子树的右连续区间内,则只需要看左子树
        }
    }
    else
    {
        if(pos<=tree[root<<1|1].l+tree[root<<1|1].ls-1)//因为pos>mid,看右子树,tree[root<1|1].l+tree[root<<1|1].ls-1代表右子树左连续区间的右边界值,如果pos在右子树的左连续内,则要看左子树的右连续有多长
        {
            return query(root<<1|1,pos)+query(root<<1,mid);
        }
        else
        {
            return query(root<<1|1,pos);//如果不在右子树的左连续区间内,则只需要看右子树
        }

    }
}
int main( )
{
    int n,q,x;
    char str[2];
    while(~scanf("%d%d",&n,&q))
    {
        while(!s.empty( ))
        {
            s.pop( );
        }
        build(1,n,1);
        //print(1,n,1);
        while(q--)
        {
            cin>>str;
            if(str[0]=='D')
            {
                cin>>x;
                s.push(x);
                update(1,x,0);
                //print(1,n,1);
            }
            if(str[0]=='Q')
            {
                cin>>x;
                cout<<query(1,x)<<endl;
            }
            if(str[0]=='R'&&!s.empty( ))
            {
                x=s.top( );
                s.pop( );
                update(1,x,1);
                //print(1,n,1);
            }

        }

    }
    return 0;
}
上一篇下一篇

猜你喜欢

热点阅读