HDU 1540(线段树)


Sample Input
7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4
Sample Output
1
0
2
4

题意很好懂,就是查询和某个点连续相邻点的个数。

这个题非常适合新手做,能够对于线段树的区间合并有更好的理解,大家看完思路后一定要自己去写啊,一定不要模仿别人的代码。

显然,又是分治法了,不过我觉得这道题分治法的思想特别明显。

这道题我们主要有两个函数,修改和查询,修改函数很普通,就和平常的差不多,查询其实也和普通的查询差不多,只是加了一些判断条件,这样,我们算法的复杂度就为 O( n*log(n) ),在n = 50000 时,nlog(n)也才 1e6,很显然是符合题目时间要求的。

那么这道题的关键是如何设计线段树每一个点的值,即设置什么,如果做过线段树区间合并的同学可能知道,有两个特别重要的值,一个是从左端开始的,连续到达的最右边,  还有一个是,从右端开始,连续到达的最左边。

有了这两个量,我们就可以进行区间合并了,因为线段树递归时都是两段,左边一段和右边一段,然后我们写判断条件来判断他们是否可以合并,并且更新出新的值。

有了这个两个量之后,查询就可以很方便了(虽说是方便,但是还是得动脑筋想,我也是想了两三个小时才想出来),

查询的框架还是基础的点查询,然后,①如果这个点在当前的左子树里面,就判断一下,判断右子树的值能不能加到结果ans里面去,成立的条件是:对于左子树从右端开始先左的区间,它是覆盖查询点的,这个条件成立表示,查询点可以直接扩展到右子树,那么直接把右子树存的东西加一下就可以了,然后继续对左子树递归。

②如果这个点在当前的右子树里面,就要判断是否可以和左子树连通,方法类似上一步①

为了更好的理解查询的过程,我画了一个图并把样例结合着图讲一下过程。


最初始的线段树

 


        destroy  3

       


        destroy   6        



后面的destroy也一样。


下面讲一下查询的过程,对于样例中第一个Q 4查询,我们首先在最上面的点,即1-7,然后m = (1+7)/2 = 4,所以被分为两个区间,1-4,和 5-7,这时候判断一下,看5-7的值是否可以加进去,所以我们需要判断1-4从右开始的区间是否覆盖4,结果发现覆盖了,所以答案加上5-7存的值(但是在这个案例中为0),然后我们继续对1-4区间递归,分成1-2,3-4两个区间,4在右边一个区间,判断左区间,发现不能连通左区间,因为3没有了,所以我们继续对3-4递归,最好到了4,返回1,结果就为1。

下面是代码:

    

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<list>
#define inf 0x3f3f3f3f
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
const int maxn = 5e4+6;
struct spe
{
    int mleft;  //从左端开始的最大区间长度
    int mright; //从右端开始的最大区间长度 
    int leftright;//从左端开始的右边界
    int rightleft;  //从右端开始的左边界
    int l,r;    //本来的范围
};
spe tree[maxn<<2];      //本人线段树采用很多位运算,请习惯,位运算可以提高速度
vector<int> v;  //方便repair
int n,m;
void change( int rt )
{
    //如果左边可以全部和右边结合
    if( tree[rt<<1].mleft && tree[rt<<1].leftright == tree[rt<<1].r && tree[rt<<1|1].mleft )
    {
        tree[rt].mleft = tree[rt<<1].mleft+tree[rt<<1|1].mleft;
        tree[rt].leftright = tree[rt<<1|1].leftright;
    }
    else
    {
        tree[rt].mleft = tree[rt<<1].mleft;
        tree[rt].leftright = tree[rt<<1].leftright;
    }
    //如果右边可以全部和左边结合
    if( tree[rt<<1|1].mright && tree[rt<<1|1].rightleft == tree[rt<<1|1].l && tree[rt<<1].mright )
    {
        tree[rt].mright = tree[rt<<1].mright+tree[rt<<1|1].mright;
        tree[rt].rightleft = tree[rt<<1].rightleft;
    }
    else
    {
        tree[rt].mright = tree[rt<<1|1].mright;
        tree[rt].rightleft = tree[rt<<1|1].rightleft;
    }
    tree[rt].l = tree[rt<<1].l;
    tree[rt].r = tree[rt<<1|1].r;
}
void build( int l,int r,int rt )
{
    if( l == r )
    {
        tree[rt].mleft = tree[rt].mright = 1;
        tree[rt].leftright = tree[rt].rightleft = l;
        tree[rt].l = tree[rt].r = l;
        return;
    }
    int m = (l+r)>>1;
    if( m >= l )
        build(l,m,rt<<1);
    if( m < r )
        build(m+1,r,rt<<1|1);
    change(rt);
}

void updatepoint( int l,int r,int rt,int pos,int val )
{
    if( l == r )
    {
        tree[rt].mleft = tree[rt].mright = val;
        return;
    }
    int m = (l+r)>>1;
    if( m >= pos )
        updatepoint(l,m,rt<<1,pos,val);
    else updatepoint(m+1,r,rt<<1|1,pos,val);
    change(rt);
}
int query( int l,int r,int rt,int pos )
{
    if( l == r )
        return tree[rt].mleft;
    int m = (l+r)>>1;
    int ans = 0;
    if( m >= pos )
    {
        if( tree[rt<<1].mright && tree[rt<<1].rightleft <= pos && tree[rt<<1|1].mleft )
            ans += tree[rt<<1|1].mleft;
        ans += query(l,m,rt<<1,pos);
    }
    else
    {
        if( tree[rt<<1|1].mleft &&  tree[rt<<1|1].leftright >= pos && tree[rt<<1].mright )
            ans += tree[rt<<1].mright;
        ans += query(m+1,r,rt<<1|1,pos);
    }
    return ans;
}
int main()
{
    while( scanf("%d %d",&n,&m) == 2 )
    {
        v.clear();
        build(1,n,1);
        char s[5];
        int pos;
        while( m-- )
        {
            scanf("%s",s);
            if( s[0] == 'D' )
            {
                scanf("%d",&pos);
                v.push_back(pos);
                updatepoint(1,n,1,pos,0);
            }
            else if( s[0] == 'R' )
            {
                if( v.size() == 0 )
                    continue;
                pos = v.back();
                v.pop_back();
                updatepoint(1,n,1,pos,1);
            }
            else if( s[0] == 'Q' )
            {
                scanf("%d",&pos);
                int t = query(1,n,1,pos);
                printf("%d\n",t);
            }
           // for( int i = 1 ; i <= 30 ; i++ )
             //   cout<<i<<" leftright "<<tree[i].leftright<<" rightleft "<<tree[i].rightleft<<endl;
        }
    }
    return 0;
}
/*



*/

猜你喜欢

转载自blog.csdn.net/qq_39627843/article/details/80539179
今日推荐