hdu 1698 Just a Hook 线段树求解

Just a Hook

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Problem Description
In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length.



Now Pudge wants to do some operations on the hook.

Let us number the consecutive metallic sticks of the hook from 1 to N. For each operation, Pudge can change the consecutive metallic sticks, numbered from X to Y, into cupreous sticks, silver sticks or golden sticks.
The total value of the hook is calculated as the sum of values of N metallic sticks. More precisely, the value for each kind of stick is calculated as follows:

For each cupreous stick, the value is 1.
For each silver stick, the value is 2.
For each golden stick, the value is 3.

Pudge wants to know the total value of the hook after performing the operations.
You may consider the original hook is made up of cupreous sticks.
 

Input
The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 10 cases.
For each case, the first line contains an integer N, 1<=N<=100,000, which is the number of the sticks of Pudge’s meat hook and the second line contains an integer Q, 0<=Q<=100,000, which is the number of the operations.
Next Q lines, each line contains three integers X, Y, 1<=X<=Y<=N, Z, 1<=Z<=3, which defines an operation: change the sticks numbered from X to Y into the metal kind Z, where Z=1 represents the cupreous kind, Z=2 represents the silver kind and Z=3 represents the golden kind.
 

Output
For each case, print a number in a line representing the total value of the hook after the operations. Use the format in the example.
 

Sample Input
1
10
2
1 5 2
5 9 3
 

Sample Output

Case 1: The total value of the hook is 24.

题意:有 t 组测试数据,n 为钩子长度(1<=n<=100000),m 为操作的次数。初始时,每个钩子的价值为1,操作由三个数字组成x,y,z表示把区间[x,y]的钩子变成的价值变成z(1代表铜,2银,3金)。

使用 lazy 懒惰标记来节省时间

注意:建树部分

void build_tree(int id,int l,int r)
{
tree[id].lazy=0; //      因为要多次建树,所以初始化时不能仅仅更新叶节点的lazy 要将lazy全部清零
if (l>=r)
{
tree[id].val=1;
return;
}

int mid=(l+r)/2;
build_tree(lson,l,mid);
build_tree(rson,mid+1,r);
push_up(id);
return ;
}
另外,注意输入输出格式

具体代码实现:
#include<iostream>
#include<algorithm>
#include<stdio.h>
#define lson (id*2)
#define rson (id*2+1)
using namespace std;
struct edge{
int val;
int lazy;
}tree[800005];
int n,ans=0,m,t;
void push_up(int id)
{
tree[id].val=tree[lson].val+tree[rson].val;
return ;
}
void push_down(int id,int l,int r)
{
int mid=(l+r)/2;
if (tree[id].lazy==0) return ;
tree[lson].val=tree[id].lazy*(mid-l+1);
tree[rson].val=tree[id].lazy*(r-mid);
tree[lson].lazy=tree[id].lazy;
tree[rson].lazy=tree[id].lazy;
tree[id].lazy=0;
return ;
}
void build_tree(int id,int l,int r)
{
tree[id].lazy=0;
if (l>=r)
{
tree[id].val=1;
return;
}
int mid=(l+r)/2;
build_tree(lson,l,mid);
build_tree(rson,mid+1,r);
push_up(id);
return ;
}
void add_tree(int id,int l,int r,int L,int R,int v)
{
if (l>=L&&r<=R)
{
tree[id].val=(r-l+1)*v;
tree[id].lazy=v;
return ;
}
int mid=(l+r)/2;
push_down(id,l,r);
if (mid>=L)
add_tree(lson,l,mid,L,R,v);
if (mid+1<=R)
add_tree(rson,mid+1,r,L,R,v);
push_up(id);
return ;
}
void query_tree(int id,int l,int r,int L,int R)
{
if (l>=L&&r<=R)
{
ans+=tree[id].val;
return ;
}
int mid=(l+r)/2;
push_down(id,l,r);

if (mid>=L) query_tree(lson,l,mid,L,R);
if (mid+1<=R) query_tree(rson,mid+1,r,L,R);

push_up(id);
return ;
}
int main()
{
cin>>t;
int num=0;
while(num<t)
{
num++;
scanf("%d",&n);
build_tree(1,1,n);
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add_tree(1,1,n,a,b,c);
}
cout<<"Case "<<num<<": The total value of the hook is "<<tree[1].val<<"."<<endl;
}
}
<think>我们正在查找杭电OJ题目HDU1698的相关信息。根据引用内容,我们并没有直接看到HDU1698的题目内容,但我们可以根据题目编号1698推测这是一道关于线段树的题目(题目名为“Just a Hook”),因为它在杭电OJ中是一个经典的线段树应用问题。 题目大意: 通常,HDU1698的题目描述是:有一个长度为n的钩子,初始每个部分都由铜制成(价值为1)。然后有若干操作,每个操作将区间[a, b]的钩子部分替换成另一种材料(银价值为2,金价值为3)。最后要求整个钩子的总价值。 解题思路: 使用线段树进行区间更新(懒惰标记)。线段树的每个节点维护该区间内的材料类型(或者总价值,由于材料一致,所以也可以维护材料类型和区间长度来计算价值)。更新操作需要懒惰标记来优化,避免每次都更新到叶子节点。 代码结构: 1. 线段树节点:通常包括该区间内的总价值,以及懒惰标记(表示该区间被统一设置成的材料类型)。 2. 建树:初始化,整个区间都是铜,即价值为1。 3. 更新:将区间[a, b]设置成材料c(即价值c),更新线段树,并使用懒惰标记。 4. 查询:通常最后查询整个区间的总价值(根节点的值)。 参考引用[2]和[5]中提供了C++代码的示例,我们可以用类似的风格编写。 下面是我编写的HDU1698题目的C++代码(带注释): ```cpp #include <iostream> #include <cstdio> #define lson l, mid, rt<<1 #define rson mid+1, r, rt<<1|1 using namespace std; const int MAXN = 100000; int tree[MAXN<<2]; // 存储线段树,每个节点表示该区间的总价值 int lazy[MAXN<<2]; // 懒惰标记,记录该区间被设置成什么材料 void pushUp(int rt) { // 向上更新:节点的值等于左右儿子节点值的和 tree[rt] = tree[rt<<1] + tree[rt<<1|1]; } void pushDown(int rt, int len) { // 向下更新:如果当前节点有懒惰标记,则更新子节点 if (lazy[rt]) { // 更新左儿子:左儿子区间长度为len - (len>>1) lazy[rt<<1] = lazy[rt]; lazy[rt<<1|1] = lazy[rt]; // 更新左儿子的总价值:材料值乘以区间长度 tree[rt<<1] = lazy[rt] * (len - (len>>1)); tree[rt<<1|1] = lazy[rt] * (len>>1); // 清除当前节点的懒惰标记 lazy[rt] = 0; } } void build(int l, int r, int rt) { // 初始化懒惰标记 lazy[rt] = 0; if (l == r) { // 叶子节点,初始化为1(铜) tree[rt] = 1; return; } int mid = (l + r) >> 1; build(lson); build(rson); pushUp(rt); // 向上更新 } void update(int L, int R, int c, int l, int r, int rt) { // [L,R]为更新区间,c为要设置的材料值 if (L <= l && r <= R) { // 当前区间完全包含在更新区间内 lazy[rt] = c; tree[rt] = c * (r - l + 1); // 更新当前区间的总价值 return; } pushDown(rt, r - l + 1); // 向下更新,传递懒惰标记 int mid = (l + r) >> 1; if (L <= mid) update(L, R, c, lson); if (R > mid) update(L, R, c, rson); pushUp(rt); // 向上更新 } int main() { int T, n, q; scanf("%d", &T); for (int cas = 1; cas <= T; cas++) { scanf("%d", &n); build(1, n, 1); // 建树,初始化 scanf("%d", &q); while (q--) { int a, b, c; scanf("%d%d%d", &a, &b, &c); update(a, b, c, 1, n, 1); // 更新区间[a,b]为材料c } printf("Case %d: The total value of the hook is %d.\n", cas, tree[1]); } return 0; } ``` 注意:题目要求输出格式为“Case X: The total value of the hook is Y.”,其中X是测试用例编号,Y是总价值。 §§相关问题§§ 1. 线段树在区间更新中的应用原理是什么? 2. 除了线段树,还有哪些方法可以解决HDU1698这类问题? 3. 杭电OJ上还有哪些经典的线段树题目推荐? </think>根据您的要求,我将为您解析杭电OJ题目HDU1698Just a Hook》,这是一道经典的线段树区间更新问题。 ### 题目解析 **题目描述** 初始有一条由 $n$ 段组成的金属钩($1 \leq n \leq 100,000$),每段材质为铜(价值1)。需要执行 $Q$ 次操作($1 \leq Q \leq 100,000$),每次操作将区间 $[a,b]$ 的材质修改为银(价值2)或金(价值3)。最终计算整个钩子的总价值。 **核心算法** 使用**线段树**配合**懒惰标记(Lazy Propagation)** 高效处理区间更新: 1. **线段树结构** - 每个节点存储区间 $[l,r]$ 的总价值和材质标记 - 初始值:所有节点价值为1(铜) 2. **懒惰标记优化** - 更新时若区间完全覆盖,直接修改节点值并打上懒惰标记 - 查询/更新子区间前下传标记,避免重复操作 3. **时间复杂度** $\Theta(Q \log n)$,满足大规模数据要求 ### 参考代码(C++) ```cpp #include <iostream> #include <cstdio> #define lson l, mid, rt<<1 #define rson mid+1, r, rt<<1|1 using namespace std; const int MAXN = 100005; int tree[MAXN<<2]; // 线段树数组 int lazy[MAXN<<2]; // 懒惰标记 // 更新子节点 void pushDown(int rt, int len) { if (lazy[rt]) { lazy[rt<<1] = lazy[rt]; lazy[rt<<1|1] = lazy[rt]; tree[rt<<1] = lazy[rt] * (len - (len>>1)); tree[rt<<1|1] = lazy[rt] * (len>>1); lazy[rt] = 0; } } // 构建线段树 void build(int l, int r, int rt) { lazy[rt] = 0; if (l == r) { tree[rt] = 1; return; } int mid = (l + r) >> 1; build(lson); build(rson); tree[rt] = tree[rt<<1] + tree[rt<<1|1]; } // 区间更新 void update(int L, int R, int val, int l, int r, int rt) { if (L <= l && r <= R) { lazy[rt] = val; tree[rt] = val * (r - l + 1); return; } pushDown(rt, r - l + 1); int mid = (l + r) >> 1; if (L <= mid) update(L, R, val, lson); if (R > mid) update(L, R, val, rson); tree[rt] = tree[rt<<1] + tree[rt<<1|1]; } int main() { int T, n, q; scanf("%d", &T); for (int cas = 1; cas <= T; ++cas) { scanf("%d%d", &n, &q); build(1, n, 1); // 初始化钩子 while (q--) { int a, b, c; scanf("%d%d%d", &a, &b, &c); update(a, b, c, 1, n, 1); //更新区间 } printf("Case %d: The total value of the hook is %d.\n", cas, tree[1]); } return 0; } ``` ### 关键知识点 1. **线段树结构设计** 节点存储区间值和懒惰标记,空间开原始数据4倍 2. **懒惰标记工作流程** ```mermaid graph TD A[区间更新请求] --> B{区间完全覆盖?} B -->|是| C[更新当前节点并打标记] B -->|否| D[下传已有标记] D --> E[递归更新子区间] E --> F[合并子节点值] ``` 3. **边界处理技巧** - 使用位运算加速:`rt<<1` 代替 `2*rt` - 区间中点计算:`mid = (l + r) >> 1` ### 注意事项 1. 多组输入数据需要重置线段树和懒惰标记 2. 输出格式必须严格匹配题目要求(包含`Case X:`前缀) 3. 初始钩子长度为n,不是固定值100,000
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值