其他分享
首页 > 其他分享> > 线段树

线段树

作者:互联网

 

 大体长这个样子

特点:

任意两个节点,要么是包含关系,要么没有公共部分,不存在重叠或相交。

给定一个叶子节点p,从该节点到根节点的所有节点所代表的区间都包含p。

存储方式:静态数组存储,左孩子*2,右孩子*2+1本人不会指针,写不出指针写法


操作:

update:

利用子节点信息更新父节点信息

代码:

tree[k].sum=tree[lc].sum+tree[rc].sum;

pchange 点修改:

跟树状数组差不多,不多讲

 1 void pchange(ll now,ll l,ll r,ll x,ll v)
 2 {
 3     if(l==r)
 4     {
 5         t[now].date+=v;
 6         return;
 7     }
 8     ll mid=(l+r)>>1;
 9     if(x<=mid)
10     {
11         pchange(t[now<<1],l,mid,x,v);
12     }
13     else
14     {
15         pchange(t[now<<1|1],mid+1,r,x,v);
16     }
17     update(now);
18 }
View Code

query 区间查询:

如果当前节点所代表的区间被要查询的区间包含,则直接返回,否则,忽略;如果相交,那就二分,把查询任务交给子节点就行了

代码:

 1 ll query(ll now,ll l,ll r,ll lr,ll rr)
 2 {
 3     if(lr<=l&&r<=rr)
 4     {
 5         return t[now].date;
 6     }
 7     ll mid=(l+r)>>1,ans=0;
 8     if(lr<=mid)
 9     {
10         ans+=query(t[now<<1],l,mid,lr,rr);
11     }
12     if(rr>mid)
13     {
14         ans+=query(t[now<<1|1],mid+1,r,lr,rr);
15     }
16     return ans;
17 }
View Code

rchange 区间修改:

引入一个新变量delta,含义为已对当前点做过,但还没对当前点的子节点做过。(称之为懒标记)

每次修改、查询之前,都要下传懒标记,调用pushdown函数,下放标记。

pushdown函数代码:

 1 ll pushout(int now,int x)
 2 {
 3     int l=t[now].l,r=t[now].r;
 4     if(l==x&&r==x)
 5     {
 6         return t[now].date;
 7     }
 8     ll mid=(l+r)>>1;
 9     pushdown(now,l,r,mid);
10     if(x<=mid)    return pushout(t[now<<1],x);
11     else    return pushout(t[now<<1|1],x);
12 }
View Code

rchange函数代码:

 1 void rchange(int now,int l,int r,int lrange,int rrange,ll v)
 2 {
 3     int cl=t[now<<1],cr=t[now<<1|1];
 4     if(lrange<=l&&r<=rrange)
 5     {
 6         delta[now]+=v;
 7         t[now].date+=(r-l+1)*v;
 8         return;
 9     }
10     int mid=(l+r)>>1;
11     pushdown(now,l,r,mid);
12     if(lrange<=mid)
13     {
14         rchange(cl,l,mid,lrange,rrange,v);
15     }
16     if(rrange>mid)
17     {
18         rchange(cr,mid+1,r,lrange,rrange,v);
19     }
20     update(now);
21 }
View Code

若区间修改后在进行区间查询,代码中也要加pushdown()

 1 ll query(int p,int l,int r,int lrange,int rrange)
 2 {
 3     ll ans=0;
 4     int mid=(l+r)>>1,cl=p<<1,cr=p<<1|1;
 5     if(lrange<=l&&r<=rrange)
 6     {
 7         return t[p].date;
 8     }
 9     pushdown(p,l,r,mid);
10     if(lrange<=mid)
11     {
12         ans+=query(cl,l,mid,lrange,rrange);
13     }
14     if(rrange>mid)
15     {
16         ans+=query(cr,mid+1,r,lrange,rrange);
17     }
18     return ans;
19 }
View Code

pushout单点查询:

 1 ll pushout(int now,int x)
 2 {
 3     int l=t[now].l,r=t[now].r;
 4     if(l==x&&r==x)
 5     {
 6         return t[now].date;
 7     }
 8     ll mid=(l+r)>>1;
 9     pushdown(now,l,r,mid);
10     if(x<=mid)    return pushout(t[now<<1],x);
11     else    return pushout(t[now<<1|1],x);
12 }
View Code

线段树的两个最重要的步骤——pushdown和update

所有的修改函数,最后都要update

大部分操作,开头都要pushdown


线段树相对于树状数组,功能更强大,在区间修改区间查询这一操作中,线段树比树状数组更好理解尽管代码长,但考场上可以临时发挥

以下代码为区间修改区间查询代码,各种树都可以套用以下代码 感谢respect_lowsmile(洛谷名)的分享!

代码:

 1 #include<iostream>
 2 #define int  long long
 3 using namespace std;
 4 const int N=1e5+5;
 5 struct node
 6 {
 7     int sum,lazy,len;
 8 };
 9 node tree[N*4];
10 int a[N];
11 int n,m,x,y,k,pd;
12 void lazy(int k,int v)
13 {
14     tree[k].lazy+=v;
15     tree[k].sum+=tree[k].len*v;
16 }
17 void pushdown(int k)
18 {
19     if(tree[k].lazy==0) return ;
20     int lc=k<<1,rc=k<<1|1;
21     lazy(lc,tree[k].lazy);
22     lazy(rc,tree[k].lazy);
23     tree[k].lazy=0;
24     return ;
25 }
26 void build(int k,int l,int r)
27 {
28     tree[k].len=r-l+1;
29     if(l==r)
30     {
31         tree[k].sum=a[l];
32         return ;
33     }
34     int mid,lc,rc;
35     mid=(l+r)>>1,lc=k<<1,rc=k<<1|1;
36     build(lc,l,mid);
37     build(rc,mid+1,r);
38     tree[k].sum=tree[lc].sum+tree[rc].sum;
39     return ;
40 }
41 void update(int k,int l,int r,int L,int R,int v)
42 {
43     if(l>=L&&r<=R)
44         return lazy(k,v);
45     pushdown(k);
46     int mid,lc,rc;
47     mid=(l+r)>>1,lc=k<<1,rc=k<<1|1;
48     if(L<=mid)
49         update(lc,l,mid,L,R,v);
50     if(R>mid)
51         update(rc,mid+1,r,L,R,v);
52     tree[k].sum=tree[lc].sum+tree[rc].sum;
53 }
54 int query(int k,int l,int r,int L,int R)
55 {
56     int res=0;
57     if(l>=L&&r<=R)
58         return tree[k].sum;
59     pushdown(k);
60     int mid,lc,rc;
61     mid=(l+r)>>1,lc=k<<1,rc=k<<1|1;
62     if(L<=mid) res+=query(lc,l,mid,L,R);
63     if(R>mid) res+=query(rc,mid+1,r,L,R);
64     return res;
65 }
66 signed main()
67 {
68     scanf("%lld %lld",&n,&m);
69     for(int i=1;i<=n;++i)
70       scanf("%lld",&a[i]);
71     build(1,1,n);
72     for(int i=1;i<=m;++i)
73     {
74         scanf("%lld",&pd);
75         if(pd==1)
76         {
77             scanf("%lld %lld %lld",&x,&y,&k);
78             update(1,1,n,x,y,k);
79         }
80         if(pd==2)
81         {
82             scanf("%lld %lld",&x,&y);
83             printf("%lld\n",query(1,1,n,x,y));
84         }
85     }
86     return 0;
87 }
View Code

如果对你有帮助,可以点一下赞吗?也可以关注一下呀QWQ,写的不好,dalao勿喷,谢谢!QAQ

 

标签:int,线段,tree,mid,pushdown,now,ll
来源: https://www.cnblogs.com/yifan0305/p/16428009.html