您的位置:首页 > 其它

hdu 1698

2014-09-02 20:05 274 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1698




题目大意:

有一只锚,这只锚由许多连续的棍子相连而成。
   共有3种材质的棍子:铜、银、金,它们对应的权值分别是1、2、3
   要执行一些操作,每一次操作会将一个连续区间内的棍子的材质改变为某一个材质。要求:在所有的操作完成之后,锚上所有棍子的总权值是多少?

思路:

   区间 成段 更新问题

     刚刚接触线段树,就碰到这个问题,折腾了好一段时间。

  由于一直用数组来做。捣鼓了一下,终于写出了生涯中第一棵显式线段树,使用链式存储。然而,由于我的算法比较笨,每次更新到最底层的叶子, 又返回更新到根。果然还是超时了。

  后来在网上学习到 Lazy(懒惰松弛) 的思想,就是我们不必每次彻底更新整棵树,只需要在刚好小于等于搜索区间的那些节点处做个标记(-1/1/2/3)就好了。-1 代表这个节点对应区间内的hooks有不止一种权值,1 代表这个节点对应区间的hooks的权值都是1,2代表这个节点对应区间的hooks的权值都是2,3代表...都是3。

    

    接下来只剩下2个问题需要解决:

①.对给定区间进行Lazy更新

[b]②.输出总权值
[/b]



① 对给定区间进行更新:

假定要更新的区间是[b][fr,to)   (算法中默认区间为左闭右开)
[/b]

           void update(int fr , int to , node *& t , int value);

         (节点t对应的区间,以下简称为节点区间)

         1. 若[fr,to) 与 节点区间不相交,或者节点区间的值 与 value 相等,就不用更新了。

         2. 若[fr,to)恰好包括节点区间 而且 节点区间的值与value不等。则令 节点区间的值为value就好了。

         3.  1, 2的情况都不满足的情况下:

             ① 若节点区间的值为-1,即节点区间内的hooks有不止一种权值。则递归update函数至其两个儿子。

             ② 若节点区间的值不为-1且又与value不相等。那么我们要进行push down 操作。push down 操作就是将节点的两个儿子节点的值设为节点的值。然后将节点的值
赋为 -1,递归两个子节点。为什么要这样做呢 ?直接把节点的值赋为value 可不可以呢 ? 

当然不可以!

                       

         为说明问题所在,这里举一个简单的例子:

         假设一棵简单的线段树,总区间[0,2),初始化每个基本节点的值为1。

       现将[0,2)的区间赋为2。对其进行Lazy松弛得到下图1:

                         

            


 

          现在,我们尝试将区间[1,1]的值更新为3,对比有push down 和 没有push down 的情况:

 

没有 push down的情况:







由于没有push down,导致[0,1)的值被修改成了3,错误!

 

 

有push down的情况:



  




第一步:将2个子节点赋为2,再将父节点赋为-1

       


    第二步:递归到两个子节点:

    
         ①. 递归到[0,1)后,由于[0,1)与[1,1]不相交。值不变仍为2

 ②. 递归到[1,2)后,由于[1,2)恰好能包括[1,1],值改为3

 
              正确 !

② 输出总权值:

    int print(node *& root)

    很自然的,我们定义sum = 0,从根节点开始递归下去:

 ① 若当前节点的标记 tag = -1,说明它子节点的类型不唯一,递归它的两个子节点.

        ② 若当前节点的标记 tag != -1,说明它子节点的类型唯一,sum += 节点区间长度 x tag.

                  

算法步骤:

① 初始化,构造线段树,我的代码中使用递归构造。 复杂度:O(N)

② k次更新操作,每次更新。复杂度:至多O(logN)

③ 递归树,累加有用的值。复杂度:O(N)

总的复杂度:O(N + klogN)

运行结果:

Accept

源程序:

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

//本题中的线段树不再是完美二叉树,也不是完全二叉树 (因为允许最后一层的叶子可以不连续地排在左边).
//尝试显式定义线段树 !
int N;

struct node{
int l,r;
node* left, *right;
int tag;
node()
{
left = right = NULL;
tag = 1;
}
node(int l, int r)
{
left = right = NULL;
this->l = l;
this->r = r;
tag = 1;
}
};

// 递归构造线段树
void init(node * root) {
stack<node*>s;
s.push(root);
while(!s.empty())
{
node* tmp = s.top();
s.pop();
if(tmp->r - tmp->l == 1)
continue;
else
{
int L = tmp->l, R = tmp->r;
node* lchild = new node(L , (L+R)/2);
node* rchild = new node((L+R)/2 , R);
tmp->left = lchild;
tmp->right = rchild;
s.push(lchild);
s.push(rchild);
}
}
}

// 使用 lazy 技术 更新节点的值
// 注意:使用了 push down 技术

void update(int fr, int to , node *& t , int val) {
if(fr >= t->r || to <= t->l || t->tag == val)
return;
else if(fr <= t->l && to >= t->r)
{
t->tag = val;
}
else
{
if(t->tag == -1)
{
update(fr,to,t->left,val);
update(fr,to,t->right,val);
}
// push down
else
{
if(t->left)
t->left->tag = t->tag;
if(t->right)
t->right->tag = t->tag;
t->tag = -1;
update(fr,to,t->left,val);
update(fr,to,t->right,val);
}

}
}

int print(node*& root) {
int sum = 0;
stack<node*> s;
s.push(root);
while(!s.empty())
{
node* tmp = s.top();
s.pop();
if(tmp->tag == -1)
{
if(tmp->left)
s.push(tmp->left);
if(tmp->right)
s.push(tmp->right);
}
else
{
sum += (tmp->r-tmp->l)*tmp->tag;
}
}
return sum;
}

int main() {
int CASE, cnt = 1;
scanf("%d",&CASE);
while(CASE--)
{
int ope;
scanf("%d",&N);

// initialize the segment tree
node* root = new node(0,N);
init(root);
scanf("%d",&ope);
int fr,to,val;
for(int i = 0 ; i < ope ; i ++)
{
scanf("%d%d%d",&fr,&to,&val);
update(fr-1,to,root,val);
}
int sum = print(root);
printf("Case %d: The total value of the hook is %d.\n",cnt,sum);
cnt ++;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息