zoukankan      html  css  js  c++  java
  • 线段树

    1、建树

    a、对于二分到的每一个结点,给它的左右端点确定范围。

    b、如果是叶子节点,存储要维护的信息,再回到父节点时累计到父节点去。

    c、合并

    代码

    void build(int k,int t,int w)
    { int mid;
        if (t>w) return;
        if (t==w)
          {
            tree[k].l=t;tree[k].r=w;
            tree[k].w=a[t];
            return;
          }
        mid=(t+w)/2;
        build(k*2,t,mid);
        build(k*2+1,mid+1,w);
        tree[k].l=t;tree[k].r=w;
        tree[k].w=tree[k*2].w+tree[k*2+1].w;
    }

    单点查询

    查询一个点的状态,设待查询点为x

    a、如果当前枚举的点左右端点相等,即叶子节点,就是目标节点。

    b、如果不是,所以设查询位置为x,当前结点区间范围为了l,r,中点为mid。

    c、如果x<=mid,则递归它的左孩子,否则递归它的右孩子

    代码

    void ask(int k)
    {
        if(tree[k].l==tree[k].r) //当前结点的左右端点相等,是叶子节点,是最终答案 
        {
            ans=tree[k].w;
            return ;
        }
        int m=(tree[k].l+tree[k].r)/2;
        if(x<=m) ask(k*2);//目标位置比中点靠左,就递归左孩子 
        else ask(k*2+1);//反之,递归右孩子 
    }

    单点修改

    即更改某一个点的状态。

    结合单点查询的原理,找到x的位置;根据建树状态合并的原理,修改每个结点的状态。

    void add(int k)
    {
        if(tree[k].l==tree[k].r)//找到目标位置 
        {
            tree[k].w+=y;
            return;
        }
        int m=(tree[k].l+tree[k].r)/2;
        if(x<=m) add(k*2);
        else add(k*2+1);
        tree[k].w=tree[k*2].w+tree[k*2+1].w;//所有包含结点k的结点状态更新 
    }

    区间查询

    代码

    void sum(int k)
    {
        if(tree[k].l>=x&&tree[k].r<=y) 
        {
            ans+=tree[k].w;
            return;
        }
        int m=(tree[k].l+tree[k].r)/2;
        if(x<=m) sum(k*2);
        if(y>m) sum(k*2+1);
    }

    区间修改

    同理

    我们不要递归到每个节点。所以要有一个新的概念:懒标记。

    就像新年的时候的压岁钱,只有要用的时候才用,不要的直接给父母保管。

    所以,传下来的更改值若在一个区间里,就不再下传,修改完该节点信息后,在此节的懒标记上打一个更改值。

    当需要递归这个节点的子节点时,标记下传给子节点。这里不必管用哪个子节点,两个都传下去。

    ①当前节点的懒标记累积到子节点的懒标记中。

    ②修改子节点状态。在引例中,就是原状态+子节点区间点的个数父节点传下来的懒标记。

    ③父节点懒标记清0。这个懒标记已经传下去了,欠债还清,不用再还了。

    下传代码

    void pushdown(int k)
    {
        tree[k*2].w+=((tree[k*2].r-tree[k*2].l+1)*tree[k].f);
        tree[k*2+1].w+=((tree[k*2+1].r-tree[k*2+1].l+1)*tree[k].f);
        tree[k*2].f+=tree[k].f;
        tree[k*2+1].f+=tree[k].f;
        tree[k].f=0;
    }

    区间修改代码

    void add(int k,int t,int w)
    { int mid;
    if (t>w) return;
        if (x<=t&&w<=y) 
          {
            tree[k].w+=((w-t+1)*z);
            tree[k].f+=z;
            return ;
          }
        mid=(t+w)/2;
        if (tree[k].f) pushdown(k); 
        if (x<=mid) add(k*2,t,mid);
        if (y>mid) add(k*2+1,mid+1,w);
        tree[k].w=tree[k*2].w+tree[k*2+1].w;
    }

    单点查询代码

     void ask(int k)//单点查询
    {
        if(tree[k].l==tree[k].r)
        {
            ans=tree[k].w;
            return ;
        }
        if(tree[k].f) pushdown(k);//懒标记下传,唯一需要更改的地方
        int m=(tree[k].l+tree[k].r)/2;
        if(x<=m) ask(k*2);
        else ask(k*2+1);
    }

    区间查询代码

    int ask(int k,int t,int w)
    { int mid;
    if (t>w) return 0;
        if (x<=t&&w<=y)
          {
            return tree[k].w;
          }
        mid=(t+w)/2;
        if (tree[k].f) pushdown(k); 
        int sum=0;
        if (x<=mid)sum+=ask(k*2,t,mid);
        if (y>mid)sum+=ask(k*2+1,mid+1,w);
        tree[k].w=tree[k*2].w+tree[k*2+1].w;
        return sum;
    }

    更具体的描述 无耻地推荐一下我的博客

    所以说此题为线段树的拓展:把求区间和改为了区间最小值,所以只需要将懒标记存储的内容改为最小值,区间修改再加工一下。

    部分代码如下:

    void build(int k,int t,int w)
    { int mid;
        if (t>w) return;
        if (t==w)
          {
            tree[k].l=t;tree[k].r=w;
            tree[k].w=a[t];
            return;
          }
        mid=(t+w)/2;
        build(k*2,t,mid);
        build(k*2+1,mid+1,w);
        tree[k].l=t;tree[k].r=w;
        tree[k].w=min(tree[k*2].w,tree[k*2+1].w);
    }
    int ask(int k,int t,int w)
    { int mid;
    if (t>w) return 0;
        if (x<=t&&w<=y)
          {
            return tree[k].w;
          }
        mid=(t+w)/2;
        int sum=INT_MAX;
        if (x<=mid)sum=min(sum,ask(k*2,t,mid));
        if (y>mid)sum=min(sum,ask(k*2+1,mid+1,w));
        return sum;
    }
    int read(int &x)
    {
        char c=getchar();int f=1;
        x=0;
        while (c<'0'||c>'9')
          {
          if (c=='-') f=-1;
          c=getchar();
          }
        while (c>='0'&&c<='9')
          {
            x=x*10+(int)c-48;
            c=getchar();
          }
        return x*f;
    }
    

    时间复杂度O(n log n)O(nlogn)

    只要不卡常,速度和单调队列和RMQRMQ相差无几,也是一种求最值的方案,可以了解一下。

    完整代码:

    #include<bits/stdc++.h>
    #define N 2000010
    using namespace std;
    struct node{
        int l,r,w,f;
    }tree[N*2+1];
    int x,y,z,i,a[N],n,m;
    void build(int k,int t,int w)
    { int mid;
        if (t>w) return;
        if (t==w)
          {
            tree[k].l=t;tree[k].r=w;
            tree[k].w=a[t];
            return;
          }
        mid=(t+w)/2;
        build(k*2,t,mid);
        build(k*2+1,mid+1,w);
        tree[k].l=t;tree[k].r=w;
        tree[k].w=min(tree[k*2].w,tree[k*2+1].w);
    }
    int ask(int k,int t,int w)
    { int mid;
    if (t>w) return 0;
        if (x<=t&&w<=y)
          {
            return tree[k].w;
          }
        mid=(t+w)/2;
        int sum=INT_MAX;
        if (x<=mid)sum=min(sum,ask(k*2,t,mid));
        if (y>mid)sum=min(sum,ask(k*2+1,mid+1,w));
        return sum;
    }
    int read(int &x)
    {
        char c=getchar();int f=1;
        x=0;
        while (c<'0'||c>'9')
          {
          if (c=='-') f=-1;
          c=getchar();
          }
        while (c>='0'&&c<='9')
          {
            x=x*10+(int)c-48;
            c=getchar();
          }
        return x*f;
    }
    signed main()
    {
        //freopen(".in","r",stdin);
        //freopen(".out","w",stdout);
        n=read(n);m=read(m);
        memset(a,INT_MAX,sizeof(a));
        for (int i=1;i<=n;i++) a[i]=read(a[i]);
        build(1,1,n);
        for (int i=1;i<=n;i++)
          { int c;
            x=i-m;y=i-1;
            if (x<=0) x=1;
            if (x>y) 
              {
                printf("0
    ");
                continue;
                }
            printf("%d
    ",ask(1,1,n));
          }
    
    }
    • 线段树求区间最小
    •  1 #include<iostream>
       2 #include<cmath>
       3 #include<cstdio>
       4 using namespace std;
       5 struct sb
       6 {
       7     int l,r,minn;
       8 }t[20000010*2];
       9 int read(int &x)
      10 {
      11     char c=getchar();int f=1;
      12     x=0;
      13     while (c<'0'||c>'9')
      14       {
      15       if (c=='-') f=-1;
      16       c=getchar();
      17       }
      18     while (c>='0'&&c<='9')
      19       {
      20         x=x*10+(int)c-48;
      21         c=getchar();
      22       }
      23     return x*f;
      24 }
      25 void build(int k,int a,int b)
      26 {
      27     t[k].l=a; t[k].r=b;
      28     if(a==b) { t[k].minn=read(t[k].minn); return; }    
      29     int mid=(a+b)>>1;
      30     build(k<<1,a,mid);
      31     build(k<<1|1,mid+1,b);
      32     t[k].minn=min(t[k<<1|1].minn,t[k<<1].minn);
      33 }
      34 int find(int k,int a,int b)
      35 {
      36     if (t[k].l==a&&t[k].r==b) return t[k].minn;
      37     int mid=(t[k].l+t[k].r)>>1;
      38     if (b<=mid) return find(k<<1,a,b);
      39     else if (a>mid) return find(k<<1|1,a,b);
      40     else
      41       return min(find(k<<1,a,mid),find(k<<1|1,mid+1,b)); 
      42 }
      43 int main ()
      44 {
      45     int n,m;
      46     cin>>n>>m;
      47     build(1,1,n);
      48     cout<<"0"<<endl;
      49     for (int i=2;i<=n;i++)
      50         printf("%d
      ",find(1,max(1,i-m),i-1));
      51  } 
    为何要逼自己长大,去闯不该闯的荒唐
  • 相关阅读:
    Jena学习笔记(2)——利用数据库保存本体
    在Jena框架下基于MySQL数据库实现本体的存取操作
    推荐系统数据稀疏性问题
    基于协同过滤的推荐系统
    机器学习相关——协同过滤
    学习进度条十五(第16周)
    梦断代码阅读笔记三
    梦断代码阅读笔记二
    数组最大值
    梦断代码阅读笔记一
  • 原文地址:https://www.cnblogs.com/zjzjzj/p/11129154.html
Copyright © 2011-2022 走看看