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
源程序:
题目大意:
有一只锚,这只锚由许多连续的棍子相连而成。
共有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; }
相关文章推荐
- HDU 1698 Just a Hook
- http://acm.hdu.edu.cn/showproblem.php?pid=1698
- hdu1698 线段树成段更新
- hdu 1698 屠夫(线段树成段替换)
- hdu1698——Just a Hook(线段树+区间更新)
- hdu 1698 线段树 just a “trick” *_*
- HDU-1698-Just a Hook(线段树 区间维护)
- hdu 1698 Just a Hook(线段树成段更新lazy)
- hdu 1698 Just a Hook(线段树成)
- HDU 1698 Just a Hook(成段更新)
- hdu 1698 Just a Hook
- 线段树区间更新——HDU 1698
- HDU 1698 E just a hook 线段树区间更新+lzy
- HDU - 1698 Just a Hook (线段树区间修改)
- 【HDU 1698】Just a Hook
- hdu 【1698】 Just a Hook
- Just a Hook (HDU - 1698)(线段树的区间更新-区间查询)
- hdu 1698 Just a Hook(线段树基础)
- HDU_1698_Just a Hook_线段树区间更新
- 【HDU】1698 Just a Hook