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

      真的是浅谈qwq,博主太弱,不会太多的操作的

      线段树是处理区间的一个数据结构常见得有修改和求和等操作(推荐一道题:快速红包变换)

      首先我们来说说线段树的优点,线段树支持一系列区间操作,但是线段树并不是每次都对每一个数进行操作,而是让上层把这些操作“克扣”下来,那么就有“克扣”条件了,如果要操作的区间完全包含当前区间,那么当前区间就可以神不知鬼不觉的把这些操作留到这里,不在向下操作,但是上面总有视察的时候啊,所以“克扣”不了多久又要原封不动的吐出来分发给下面的黎民百姓,当然了如果有些领导看不到的地方,也就不会下放下去了。

      23333,蒟蒻用了一个比喻来形容线段树的原理,正事由于这个,才使得线段树的每一个操作的时间复杂度是log的。

    窝自己画的,不喜勿喷。

      线段树就是酱紫的,每个叶子节点代表的原区间的一个点,而非叶子节点存的是一段区间的答案,比如和或乘积之类的。

      唔,窝发现窝根本不知道该怎么讲,不如先看一下模板吧,打几遍就能理解了

    模板:https://www.luogu.org/problemnew/show/P3372

     1 #include<iostream>
     2 #include<cstdio>
     3 #define ll long long 
     4 using namespace std;
     5 const int maxn=100005;
     6 int n,m;
     7 struct zhw{
     8     ll lazy,val;
     9 }tree[maxn<<2];
    10 int opt,x,y;
    11 ll v; 
    12 void btree(int now,int l,int r)
    13 {
    14     if(l==r)
    15     {
    16         scanf("%lld",&tree[now].val);return;
    17     }
    18     int mid=(l+r)>>1;
    19     btree(now<<1,l,mid),btree(now<<1|1,mid+1,r);
    20     tree[now].val=tree[now<<1].val+tree[now<<1|1].val;
    21 }
    22 void down(int now,int l,int r)
    23 {
    24     if(tree[now].lazy)
    25     {
    26         int mid=(l+r)>>1;
    27         tree[now<<1].lazy+=tree[now].lazy,tree[now<<1|1].lazy+=tree[now].lazy;
    28         tree[now<<1].val+=(mid-l+1)*tree[now].lazy,tree[now<<1|1].val+=(r-mid)*tree[now].lazy;
    29         tree[now].lazy=0;    
    30     }
    31 }
    32 void add(int now,int l,int r,int ql,int qr,ll v)
    33 {
    34     if(ql<=l&&qr>=r)
    35     {
    36         tree[now].lazy+=v,tree[now].val+=(r-l+1)*v;return;
    37     }
    38     int mid=l+r>>1;
    39     down(now,l,r); //标记下放
    40     if(ql<=mid)add(now<<1,l,mid,ql,qr,v);//如果不是完全被覆盖,就向下查找
    41     if(qr>mid)add(now<<1|1,mid+1,r,ql,qr,v);//分出2部分,左右分别取找
    42     tree[now].val=tree[now<<1].val+tree[now<<1|1].val;//这里需要更新一下,因为虽然当前点不能被所要修改的区间包含,但是所要修改的区间是它的一部分,所以它的值也会有所变化
    43 }
    44 ll query(int now,int l,int r,int ql,int qr)
    45 {
    46     if(ql<=l&&qr>=r)return tree[now].val;
    47     int mid=(l+r)>>1;
    48     down(now,l,r);
    49     ll ans=0;
    50     if(ql<=mid)ans+=query(now<<1,l,mid,ql,qr);
    51     if(qr>mid)ans+=query(now<<1|1,mid+1,r,ql,qr);
    52     return ans;
    53 }
    54 int main()
    55 {
    56     scanf("%d%d",&n,&m);
    57     btree(1,1,n);
    58     while(m--)//2个操作,区间修改和区间求和
    59     {
    60         scanf("%d%d%d",&opt,&x,&y);
    61         if(opt==1)
    62         {
    63             scanf("%lld",&v);
    64             add(1,1,n,x,y,v);
    65         }
    66         else
    67             printf("%lld
    ",query(1,1,n,x,y));
    68     }
    69     return 0;
    70 } 
    View Code

      唔,大概就是这样吧,其实线段树模板还是很好理解的。我最开始学线段树的时候写了一个区间修改点查询的模板,调了一天,┓( ´∀` )┏

      下面来看一些例题

    1. 洛谷2574 xor的艺术

    AKN觉得第一题太水了,不屑于写第一题,所以他又玩起了新的游戏。在游戏中,他发现,这个游戏的伤害计算有一个规律,规律如下

    1、 拥有一个伤害串为长度为n的01串。

    2、 给定一个范围[l,r],伤害为伤害串的这个范围内中1的个数

    3、 会被随机修改伤害串中的数值,修改的方法是把[l,r]中的所有数xor上1

    AKN想知道一些时刻的伤害,请你帮助他求出这个伤害

      裸的线段树qwq,01异或就是取反,只需要记录个数,每次异或就是把区间中1的个数变成区间长度-1的个数即可,代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 const int maxn=2e5+2;
     5 int n,m;
     6 struct node{
     7     int val,lazy;
     8 };
     9 node tree[maxn<<2];
    10 void btree(int now,int l,int r)
    11 {
    12     if(l==r)
    13     {
    14         scanf("%1d",&tree[now].val);
    15         return;
    16     }
    17     int mid=(l+r)>>1;
    18     btree(now<<1,l,mid);
    19     btree(now<<1|1,mid+1,r);
    20     tree[now].val=tree[now<<1].val+tree[now<<1|1].val;
    21 }
    22 void down(int now,int l,int r)
    23 {
    24     if(tree[now].lazy)
    25     {
    26         int mid=(l+r)>>1;
    27         tree[now<<1].lazy^=tree[now].lazy;
    28         tree[now<<1|1].lazy^=tree[now].lazy;
    29         tree[now<<1].val=(mid-l+1)-tree[now<<1].val;
    30         tree[now<<1|1].val=(r-mid)-tree[now<<1|1].val;
    31         tree[now].lazy=0;
    32     }
    33 }
    34 int p,x,y;//抑或0是自己,抑或1是本身 
    35 //抑或上一,0——>1,1——>0,所以1的个数是区间长度减去1的个数 
    36 void add(int now,int l,int r,int ql,int qr)
    37 {
    38     if(ql<=l&&qr>=r)
    39     {
    40         tree[now].lazy^=1;
    41         tree[now].val=(r-l+1)-tree[now].val;
    42         return;
    43     }
    44     int mid=(l+r)>>1;
    45     down(now,l,r);
    46     if(ql<=mid)add(now<<1,l,mid,ql,qr);
    47     if(qr>mid)add(now<<1|1,mid+1,r,ql,qr);
    48     tree[now].val=tree[now<<1].val+tree[now<<1|1].val;
    49 }
    50 int  query(int now,int l,int r,int ql,int qr)
    51 {
    52     if(ql<=l&&qr>=r)return tree[now].val;
    53     int mid=(l+r)>>1;
    54     int ans=0;
    55     down(now,l,r);
    56     if(ql<=mid)ans+=query(now<<1,l,mid,ql,qr);
    57     if(qr>mid)ans+=query(now<<1|1,mid+1,r,ql,qr); 
    58     return ans;
    59 }
    60 int main()
    61 {
    62     scanf("%d%d",&n,&m);
    63     btree(1,1,n);
    64     for(int i=1;i<=m;++i)
    65     {
    66         scanf("%d",&p);
    67         scanf("%d%d",&x,&y);
    68         if(p)cout<<query(1,1,n,x,y)<<endl;
    69         else add(1,1,n,x,y);
    70     } 
    71 } 
    View Code

    2.洛谷P2787 语文1(chin1)- 理理思维

    考试开始了,可是蒟蒻HansBug脑中还是一片空白。哦不!准确的说是乱七八糟的。现在首要任务就是帮蒟蒻HansBug理理思维。假设HansBug的思维是一长串字符串(字符串中包含且仅包含26个字母),现在的你,有一张神奇的药方,上面依次包含了三种操作:

    1. 获取第x到第y个字符中字母k出现了多少次

    2. 将第x到第y个字符全部赋值为字母k

    3. 将第x到第y个字符按照A-Z的顺序排序

    你欣喜若狂之时,可是他脑细胞和RP已经因为之前过度紧张消耗殆尽,眼看试卷最后还有一篇800字的作文呢,所以这个关键的任务就交给你啦!

      唔,窝感觉这是一道很好的题,因为我开始看到排序的操作直接就懵逼了,后来看了别人的题解才知道怎么做,确实很巧妙的感觉,在数据结构里开一个大熊啊为26的数组,记录每个字符的个数,而排序就用第一个和第二个操作来完成,先求出每个字符的个数,然后从0到25一次进行区间覆盖,覆盖的区间的长度就是字符出现的次数,这样就可以假装排序了,23333

      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio> 
      4 using namespace std;
      5 const int maxn=65536;
      6 char s[maxn];
      7 int lazy[maxn<<2];
      8 int n,m;
      9 struct zhw{
     10     int cnt[26];
     11     zhw(){memset(cnt,0,sizeof(cnt));};
     12 }tree[maxn<<2];
     13 zhw operator +(zhw a,zhw b)
     14 {
     15     zhw c;
     16     for(int i=0;i<26;++i)c.cnt[i]=a.cnt[i]+b.cnt[i];
     17     return c;
     18 }
     19 void btree(int now,int l,int r)
     20 {
     21     if(l==r)
     22     {
     23         tree[now].cnt[s[l]-'A']=1;return; 
     24     } 
     25     int mid=(l+r)>>1;
     26     btree(now<<1,l,mid),btree(now<<1|1,mid+1,r);
     27     tree[now]=tree[now<<1]+tree[now<<1|1]; 
     28 }
     29 int flag,x,y;
     30 char c;
     31 void down(int now,int l,int r)
     32 {
     33     if(lazy[now]!=-1)
     34     {
     35         lazy[now<<1]=lazy[now],lazy[now<<1|1]=lazy[now];
     36         tree[now<<1]=zhw(),tree[now<<1|1]=zhw();
     37         //因为是很恶心的覆盖操作的lazy,所以先清空后赋值来搞 
     38         tree[now<<1].cnt[lazy[now]]=l,tree[now<<1|1].cnt[lazy[now]]=r;
     39         lazy[now]=-1; 
     40     }
     41 } 
     42 zhw query(int now,int l,int r,int ql,int qr)
     43 {
     44     if(ql<=l&&qr>=r)return tree[now];
     45     zhw ans;
     46     int mid=(l+r)>>1;
     47     down(now,mid-l+1,r-mid);
     48     if(ql<=mid)ans=ans+query(now<<1,l,mid,ql,qr);
     49     if(qr>mid)ans=ans+query(now<<1|1,mid+1,r,ql,qr);
     50     return ans;//已经重载过加号了呀,所以ans只是一个用来记录数量的变量 
     51 }
     52 void change(int now,int l,int r,int ql,int qr,int k)
     53 {
     54     if(ql<=l&&qr>=r)
     55     {
     56         tree[now]=zhw();lazy[now]=k,tree[now].cnt[k]=r-l+1;return;
     57     }
     58     int mid=(l+r)>>1;
     59     down(now,mid-l+1,r-mid);
     60     if(ql<=mid)change(now<<1,l,mid,ql,qr,k);
     61     if(qr>mid)change(now<<1|1,mid+1,r,ql,qr,k);
     62     tree[now]=tree[now<<1]+tree[now<<1|1];
     63 }
     64 int main()
     65 {
     66     scanf("%d%d",&n,&m);scanf("%s",s+1);
     67     memset(lazy,-1,sizeof(lazy));
     68     for(int i=n;i>=1;i--)
     69         if(s[i]>='a'&&s[i]<='z')s[i]=s[i]-'a'+'A';
     70     btree(1,1,n);
     71     while(m--)
     72     {
     73         scanf("%d",&flag);
     74         if(flag==1)
     75         {
     76             scanf("%d%d",&x,&y);cin>>c;
     77             if(c<='z'&&c>='a')c=c-'a'+'A';
     78             printf("%d
    ",query(1,1,n,x,y).cnt[c-'A']);
     79         }
     80         else if(flag==2)
     81         {
     82             scanf("%d%d",&x,&y);cin>>c;
     83             if(c<='z'&&c>='a')c=c-'a'+'A';    
     84             change(1,1,n,x,y,c-'A');        
     85         }
     86         else{
     87             scanf("%d%d",&x,&y);
     88             zhw tmp=query(1,1,n,x,y);
     89             int l=0,r=x-1;
     90             for(int i=0;i<26;++i)
     91             {
     92                 if(tmp.cnt[i]!=0)
     93                 {
     94                     l=r+1,r=l+tmp.cnt[i]-1;
     95                     change(1,1,n,l,r,i);
     96                 }
     97             }
     98         }
     99     }
    100     return 0;
    101 }
    View Code

    3.洛谷P1198 [JSOI2008]最大数

    现在请求你维护一个数列,要求提供以下两种操作:

      1、 查询操作。

      语法:Q L

      功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。

      限制:L不超过当前数列的长度。(L>=0)

      2、 插入操作。

      语法:A n

      功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。

      限制:n是整数(可能为负数)并且在长整范围内。

      注意:初始时数列是空的,没有一个数。

      这也是很裸的线段树,操作1就是区间查询,操作2就是点修改,但是要注意区间长度始终要设成最大值,不能动态的变,我开始写的时候就挂在这里了qwq

     1 //一个add,一个query,简单的线段树吧 
     2 #include<iostream>
     3 #include<cstdio>
     4 using namespace std;
     5 const int maxn=200005;
     6 int m,d; 
     7 struct Ray{
     8     int val;
     9 }tree[maxn<<2];
    10 char c;
    11 int x;
    12 int n;
    13 void btree(int now,int l,int r)
    14 {
    15     tree[now].val=2147483648;
    16     if(l==r)return;
    17     int mid=(l+r)>>1;
    18     btree(now<<1,l,mid),btree(now<<1|1,mid+1,r);
    19 }
    20 int query(int now,int l,int r,int ql,int qr)
    21 {
    22     if(ql<=l&&qr>=r)
    23     {
    24 //        cout<<now<<" "<<tree[now].val<<endl;
    25         return tree[now].val; 
    26     }
    27     int mid=(l+r)>>1;
    28     int ans=2147483648;
    29     if(ql<=mid)ans=max(ans,query(now<<1,l,mid,ql,qr));
    30     if(qr>mid)ans=max(ans,query(now<<1|1,mid+1,r,ql,qr)); 
    31     return ans;
    32 }
    33 void add(int now,int l,int r,int ql,int qr,int v)
    34 {
    35     if(ql<=l&&qr>=r)
    36     {
    37         tree[now].val=v;
    38         return;
    39     }
    40     int mid=(l+r)>>1;
    41     if(ql<=mid)add(now<<1,l,mid,ql,qr,v);
    42      else add(now<<1|1,mid+1,r,ql,qr,v);
    43     tree[now].val=max(tree[now<<1].val,tree[now<<1|1].val);
    44 }
    45 int ans=0;
    46 int main()
    47 {
    48     scanf("%d%d",&m,&d);
    49     btree(1,1,200000);
    50     for(int i=1;i<=m;++i)
    51     {
    52         cin>>c;scanf("%d",&x);
    53         if(c=='Q'&&x==0)
    54         {
    55             printf("0
    ");continue;
    56         }
    57         if(c=='Q')printf("%d
    ",ans=query(1,1,200000,n-x+1,n));
    58         else{
    59             x+=ans,x%=d;
    60             add(1,1,200000,n+1,n+1,x);
    61             n++; 
    62         }
    63     }
    64     return 0;
    65 }
    View Code
    知世故而不世故,处江湖而远江湖,才是最成熟的善良
  • 相关阅读:
    MS SQL数据类型
    ASP中调用存储过程、语法、写法-sql server数据库
    系統存儲過程System stored procedures
    ASP调用带参数存储过程的几种方式
    解密SQLSERVER2000存储过程,函数,视图,触发器
    SQL Script 執行的時間
    各種正則表达式示例
    12款操作系统安装全程图解(菜鸟必备宝典)
    内存 : DDR2与DDR
    數據庫分頁
  • 原文地址:https://www.cnblogs.com/yuelian/p/8745056.html
Copyright © 2011-2022 走看看