您的位置:首页 > 其它

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

2011-08-15 16:48 399 查看
  该题也算是最基础的线段树了,由于没有很好的理解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;
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: