HDU 1698 Just a Hook 线段树成段更新

本文深入探讨了区间更新的概念及其在计算机科学领域的应用,重点介绍了如何通过线段树来优化区间更新操作,显著提高了算法效率。通过具体代码实例,详细解释了线段树的构建与更新过程,为读者提供了宝贵的实践指导。

算是我真正意义上的区间更新的第一题,我是这样理解区间更新的:

为了节省时间,在更新区间的时候不必每次都更新到叶子节点。如果当前节点的的区间被包含在查询区间内,就暂时只更新这个节点。但是如果仅仅这样,将来在下次更新的时候,如果涉及了该节点的孩子节点,就会出错。

所以有一个办法:在更新的时候,如果我们明确知道了该节点的儿子节点涉及到了将要更新的区间,就事先把它的左右儿子节点按照父节点的val更新了,由于更新是递归实现的,所以如果它的孙子节点也涉及了,孙子节点会在儿子节点的递归层被更新。

这样做肯定比每次都更新到叶子节点大大节省了时间。

AC代码:

/* ***********************************************
Author        :angon
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define mst(a,k)  memset(a,k,sizeof(a));
#define LL long long
#define maxn 100005
#define mod 100000007
/*
inline int read()
{
    int s=0;
    char ch=getchar();
    for(; ch<'0'||ch>'9'; ch=getchar());
    for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';
    return s;
}
inline void print(int x)
{
    if(!x)return;
    print(x/10);
    putchar(x%10+'0');
}
*/
int n;
struct node
{
    int l,r,v,sum; //v 代表类型,sum代表总和
}seg[maxn*4];

void build(int i,int l,int r)
{
    seg[i].l=l;
    seg[i].r=r;
    seg[i].v=1;
    if(l==r)
    {
        seg[i].sum=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;

}

void update(int i,int l,int r,int val)
{
    if(seg[i].v==val) return ; //剪枝
    if(l<=seg[i].l && r>=seg[i].r)
    {
        seg[i].v=val;
        seg[i].sum=(seg[i].r-seg[i].l+1)*val;
        return ;
    }
    if(seg[i].v>0)    //如果大于0,说明区间里面颜色一样
    {               //由上面一个if没有return可知后面必定对子树进行操作,
        seg[i<<1].v=seg[i<<1|1].v=seg[i].v;    //所以先更新孩子节点
        seg[i<<1].sum=(seg[i<<1].r-seg[i<<1].l+1)*seg[i].v;
        seg[i<<1|1].sum=(seg[i<<1|1].r-seg[i<<1|1].l+1)*seg[i].v;
        seg[i].v=0;
    }
    int mid=(seg[i].l+seg[i].r)>>1;
    if(l<=mid) update(i<<1,l,r,val);
    if(r>mid) update(i<<1|1,l,r,val);
    seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t,q,x,y,c;
    scan(t);
    int cas=1;
    while(t--)
    {
        scan(n);
        build(1,1,n);

        scan(q);
        while(q--)
        {
            scanf("%d%d%d",&x,&y,&c);
            update(1,x,y,c);
        }
        printf("Case %d: The total value of the hook is %d.\n",cas++,seg[1].sum);
    }
    return 0;
}

附kuangbin大神的代码,更加符合标准,更模版化,再看一遍也许可以帮助理解

/*
HDU 1689
线段树
成段更新
*/


#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN=100010;
struct Node
{
    int l,r;
    int lazy,tag;
    int sum;
}segTree[MAXN*3];
void Build(int i,int l,int r)
{
    segTree[i].l=l;
    segTree[i].r=r;
    segTree[i].lazy=0;
    segTree[i].tag=0;
    if(l==r)
    {
        segTree[i].sum=1;
        return;
    }
    int mid=(l+r)>>1;
    Build(i<<1,l,mid);
    Build((i<<1)|1,mid+1,r);
    segTree[i].sum=segTree[i<<1].sum+segTree[(i<<1)|1].sum;
}
void update(int i,int l,int r,int v)
{
    if(segTree[i].l==l&&segTree[i].r==r)//成段更新
    {
        segTree[i].lazy=1;
        segTree[i].tag=v;
        segTree[i].sum=(r-l+1)*v;
        return;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if(segTree[i].lazy==1)
    {
        segTree[i].lazy=0;
        update(i<<1,segTree[i].l,mid,segTree[i].tag);
        update((i<<1)|1,mid+1,segTree[i].r,segTree[i].tag);
        segTree[i].tag=0;
    }
    if(r<=mid) update(i<<1,l,r,v);
    else if(l>mid)update((i<<1)|1,l,r,v);
    else
    {
        update(i<<1,l,mid,v);
        update((i<<1)|1,mid+1,r,v);
    }
    segTree[i].sum=segTree[i<<1].sum+segTree[(i<<1)|1].sum;
}
int main()
{
      //  freopen("in.txt","r",stdin);
 //  freopen("out.txt","w",stdout);
    int x,y,z;
    int n;
    int m;
    int T;
    scanf("%d",&T);
    int iCase=0;
    while(T--)
    {
        iCase++;
        scanf("%d%d",&n,&m);
        Build(1,1,n);
        while(m--)
        {
            scanf("%d%d%d",&x,&y,&z);
            update(1,x,y,z);
        }
        printf("Case %d: The total value of the hook is %d.\n",iCase,segTree[1].sum);
    }
    return 0;
}


演示了为无线无人机电池充电设计的感应电力传输(IPT)系统 Dynamic Wireless Charging for (UAV) using Inductive Coupling 模拟了为无人机(UAV)量身定制的无线电力传输(WPT)系统。该模型演示了直流电到高频交流电的转换,通过磁共振在气隙中无线传输能量,以及整流回直流电用于电池充电。 系统拓扑包括: 输入级:使用IGBT/二极管开关连接到全桥逆变器的直流电压源(12V)。 开关控制:脉冲发生器以85 kHz(周期:1/85000秒)的开关频率运行,这是SAE J2954无线充电标准的标准频率。 耦合级:使用互感和线性变压器块来模拟具有特定耦合系数的发射(Tx)和接收(Rx)线圈。 补偿:包括串联RLC分支,用于模拟谐振补偿网络(将线圈调谐到谐振频率)。 输出级:桥式整流器(基于二极管),用于将高频交流电转换回直流电,以供负载使用。 仪器:使用示波器块进行全面的电压和电流测量,用于分析输入/输出波形和效率。 模拟详细信息: 求解器:离散Tustin/向后Euler(通过powergui)。 采样时间:50e-6秒。 4.主要特点 高频逆变:模拟85 kHz下IGBT的开关瞬态。 磁耦合:模拟无人机着陆垫和机载接收器之间的松耦合行为。 Power GUI集:用于专用电力系统离散仿真的设置。 波形分析:预配置的范围,用于查看逆变器输出电压、初级/次级电流和整流直流电压。 5.安装与使用 确保您已安装MATLAB和Simulink。 所需工具箱:必须安装Simscape Electrical(以前称为SimPowerSystems)工具箱才能运行sps_lib块。 打开文件并运行模拟。
11-06
<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
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值