HDU-1698 Just a Hook 线段树 Lazy思想

  该题也算是最基础的线段树了,由于没有很好的理解Lazy思想,导致前面为了写出一个正确的程序花了半天功夫啊。前面是这样想的,对于某一段区间,如果已经赋了值,那么后面的修改就在这个值上进行,例如前面如果1 - 3 赋为2,后面如果又有一次 2 - 3赋为1的话,那么就在 2- 3的区间上赋值为 -1,因为前面已经在1-3算作 2 了。虽说思路出来了,但后面一直还是WA,原因在于经过多次的更新后,有些应该被舍弃的值被重复利用了,比如上例中再出现一组1 - 3 赋值为3,后面的 2- 3区间值就不对了,于是又加了时间戳,最后写出来AC的代码也便是搓不可言了。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;

int N;

struct Node
{
	int l, r, val, ti;
	Node *lch, *rch;
	Node( int left, int right )
	{
		l= left, r= right, val= 0, ti= 0;
		lch= rch= NULL;
	}
	int getmid(  )
	{
		return ( l+ r )/ 2;
	}
};

void creat( Node *p )
{
	if( p->r- p->l> 1 )  // 如果该区间可以再细分
	{
		p->lch= new Node( p->l, p->getmid() );  // 左孩子为 l 到 mid
		p->rch= new Node( p->getmid(), p->r );  // 右孩子为 mid 到 r
		creat( p->lch ), creat( p->rch ); // 递归构建子区间
	}
}

void update( Node *p, int l, int r, int add, int tt, int ti )
{
	if( p->l== l&& p->r== r ) // 如果找到了一个能够来求和的区间
	{
		p->val= add;   // 其lazy值加上计算出来的增量 
	    p->ti= ti;     // 对于区间更新的节点加上时间戳
		return;        // 及时结束该次递归过程
	} 
	if( p->ti> tt ) // 如果该点是后更新的,才减去重复计算的部分
	{ 
	    add-= p->val; 
	    tt= p->ti;
	} 
	if( l>= p->getmid() ) // 如果区间在该节点的右枝
	{
		update( p->rch, l, r, add, tt, ti );
	}
	else if( r<= p->getmid() ) // 如果区间在该节点的左枝
	{
		update( p->lch, l, r, add, tt, ti );
	}
	else // 如果区间横跨两个孩子所表示的区间
	{
		update( p->lch, l, p->getmid(), add, tt, ti );
		update( p->rch, p->getmid(), r, add, tt, ti );
	}
}

void sum( Node *p, int ti, int &ans ) // 最重要的求和过程
{
    if( !p )
    {
        return;
    } 
    if( p->ti> ti )
    {
        ans+= ( p->r- p->l )* p->val;
        sum( p->lch, p->ti, ans );
        sum( p->rch, p->ti, ans );
    }
    else
    {
        sum( p->lch, ti, ans );
        sum( p->rch, ti, ans );
    }
}

void _free( Node *p )
{
	if( p->lch )
	{
		_free( p->lch );
	}
	if( p->rch )
	{
		_free( p->rch );
	}
	free( p );
}

int main()
{
	int T, ti, ans;
	scanf( "%d", &T );
	for( int t= 1; t<= T; ++t )
	{
	    ans= 0;
		scanf( "%d", &N );
		Node *r= new Node( 1, N+ 1 );
		creat( r );
		update( r, 1, N+ 1, 1, 0, 1 );  // 把第一次更新看作是初始化
		scanf( "%d", &ti );
		for( int i= 1; i<= ti; ++i )
		{
			int tl, tr, tadd;
			scanf( "%d %d %d", &tl, &tr, &tadd );
			update( r, tl, tr+ 1, tadd, 0, i+ 1 );
		}
		sum( r, -1, ans );
		printf( "Case %d: The total value of the hook is %d.\n", t, ans );
		_free( r );  // 释放所有节点
	}
	return 0;
}

  还好,重新理解了Lazy思想后,过这题就很简单了。

  代码如下:

#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;

int N;

struct Node
{
    int l ,r, val;
    Node *lch, *rch;
    Node ( int ll, int rr )
    {
        l= ll, r= rr, val= -1;
        lch= rch= NULL;
    }
    int Gm(  )
    {
        return ( l+ r )>> 1;
    }
};

void Creat( Node *p )
{
    if( p->r- p->l> 1 )
    {
        p->lch= new Node( p->l, p->Gm() );
        p->rch= new Node( p->Gm(), p->r );
        Creat( p->lch ), Creat( p->rch );
    }
}

void Update( Node *p, int l, int r, int val )
{
    if( p->val== val )
    {
        return;
    }
    if( p->l== l&& p->r== r )
    { 
        p->val= val;
        return; 
    }
    if( p->val!= -1 )
    {
        p->lch->val= p->val, p->rch->val= p->val;
        p->val= -1; // 将该节点赋为杂色
    }
    if( l>= p->Gm() )
    {
        Update( p->rch, l, r, val );
    }
    else if( r<= p->Gm() )
    {
        Update( p->lch, l, r, val );
    }
    else
    {
        Update( p->lch, l, p->Gm(), val );
        Update( p->rch, p->Gm(), r, val );
    }
}

int Sum( Node *p )
{
    if( !p )
    {
        return 0;
    }
    if( p->val!= -1 )
    {
        return ( p->r- p->l )* p->val;
    }
    else
    {
        return Sum( p->lch )+ Sum( p->rch );
    }
}

void _free( Node *p )
{
	if( p->lch )
	{
		_free( p->lch );
	}
	if( p->rch )
	{
		_free( p->rch );
	}
	free( p );
}

int main(  )
{
    int T;
    scanf( "%d", &T );
    for( int t= 1; t<= T; ++t )
    { 
        int op;
        scanf( "%d", &N );
        Node *r= new Node( 1, N+ 1 );
        Creat( r );
        scanf( "%d", &op );
        Update( r, 1, N+ 1, 1 );
        for( int i= 1; i<= op; ++i )
        {
            int ll, rr, val;
            scanf( "%d %d %d", &ll, &rr, &val );
            Update( r, ll, rr+ 1, val );
        }
        printf( "Case %d: The total value of the hook is %d.\n", t, Sum( r ) );
        _free( r );
    }
}

  网络上更高效的解法。

#include <iostream>
#include <cstdio>
using namespace std;

int data[100005][3];

int main()
{
    int t,q,n,i,j,sum,k,v;
    scanf("%d",&t);
    for(i=1;i<=t;i++)
    {
        scanf("%d%d",&n,&q);
        for(j=1;j<=q;j++)
            scanf("%d%d%d",&data[j][0],&data[j][1],&data[j][2]);
        sum=0;
        for(k=1;k<=n;k++)
        {
            v=1;
            for(j=q;j>=1;j--)
                if(data[j][0]<=k && k<=data[j][1])//寻找k所在的更新区间,若存在则更新,不存在v=1不变
                {
                    v=data[j][2];                 //若找的最后面的更新区间,则停止,因为前面的会被覆盖
                    break;
                }
            sum+=v;
        }
        printf("Case %d: The total value of the hook is %d.\n",i,sum);
    }
    return 0;
}

  

posted @ 2011-08-15 16:48  沐阳  阅读(523)  评论(0编辑  收藏  举报