线段树模板(洛谷P3372)

给定序列,支持区间加、求区间和。

线段树的基本思路(线段树模板嘛,不懂得看题解第一,dalao讲解超详细的)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int MAXN=1000001;
unsigned ll n,m,a[MAXN],ans[MAXN<<2],lazytag[MAXN<<2];

开头还用解释吗qaq

inline ll 左儿子
inline ll 右儿子
void 输入
void 传递
void 建树
void 当前处理懒标记
void 传递懒标记 
void 更新
ll 求和
//当然在数据较小的时候可以用int,看着顺眼

以上是各函数的框架,都是线段树的基本函数。

int main()
{
    输入
    建树
    while(m--)
    {
	输入操作选项
	case 1:
	{
	输入xyk 
	更新
	break; 
	} 
	case 2:
	{
	    输入xy
	    输出和
	    break; 
	}
	}
    return 0;
} 

主函数的设计思路还是比较简单的,不过要注意输入输出的优化,cincout可能会TLE吧,虽然没试过2333

接下来是填好了的主函数!一定要自己打一遍再看哦~

image

真的自己打了吗???

int main()
{
    ll op,x,y,k; 
    shuru();
    buildtree(1,1,n);
    while(m--)
    {
        scanf("%lld",&op);
        switch(a1)
        case 1:
        {
            scanf("%lld%lld%lld",&x,&y,&k);
            update(x,y,1,n,1,k);
            break; 
        } 
        case 2:
        {
            scanf("%lld%lld",&x,&y); 
            printf("%lld
",query(x,y,1,n,1));
            break;
            
    
    }
	return 0;
} 

这些函数都是啥???接着往下看吧owo

先看比较简单的找左右儿子

根据二叉树的性质

父亲节点fa的左右儿子

分别是 fa<<2 和 fa<<2|1

这是zhaungbi优秀的位运算

表示fa2 和 fa2+1

你可以先不用理解它是什么意思

记住它的含义就好了

void shuru()
{
    cin>>n>>m;
    for(ll i=1;i<=n;i++)
    scanf("%lld",&a[i]);
}

简单可爱的输入

void pushup(ll x)
{
    ans[x]=ans[leftchild(x)]+ans[rightchild(x)];
}
void buildtree(int x,int l,int r)
{
	lazytag[x]=0;
	if(l==r)
	{
		ans[x]=a[l];
		return;
	}
	ll mid=(l+r)>>1;
	build(leftchild(x),l,mid);
	build(rightchild(x),mid+1,r);
	pushup(p);
}

二分的方法建树

如果是叶子节点,那么它的值就是自己

如果是父亲节点,就是它的儿子的和

初始化懒标记为0,表示暂时没有向下传递的数据

void lazy(ll x,ll l,ll r,ll k)
{
    lazytag[x]=lazytag[x]+k;
    ans[x]=ans[x]+k*(r-l+1);
    lazytag[x]=0;
}

void pushdown(ll x,ll l,ll r)
{
	ll mid=(l+r)>>1;
	lazy(leftchild(x),l,mid,lazytag[x]);
	lazy(rightchild(x),mid+1,r,lazytag[x]);
}
void update(ll L,ll R,ll l,ll r,ll x,ll k)
{
	//[L,R]为要求查询区间,查询过程中不能变 
	if(L<=l && R>=r)
	//当前区间被查询区间完全包含 
	{
		ans[x]+=k*(r-l+1);
        lazytag[x]+=k;
        return;
	}
	//不完全包含 
	pushdown(x,l,r);//处理懒标记,排除影响 
	ll mid=(l+r)>>1;
    if(L<=mid)update(L,R,l,mid,leftchild(x),k);
    if(R>mid) update(L,R,mid+1,r,rightchild(x),k);
    pushup(x);
}
ll querysum(ll a,ll b,ll l,ll r ll x) 
{
	ll res=0;
	//当前区间被查询区间完全包含 
	if(a<=l && r<=b)return ans[p]; 
    //不完全包含 
	ll mid=(l+r)>>1;
    push_down(x,l,r);
    if(a<=mid)res+=querysum(a,b,l,mid,leftchild(x));
    if(b>mid) res+=querysum(a,b,mid+1,r,rightchild(x));
    return res;
}

接下来求和和更新的操作类似

都是判断所求区间与当前区间的包含关系

完全包含当前区间比较好办

不完全包含的情况用二分查找,降低时间复杂度owo


测试点信息

1

AC
0ms/2101KB

2

AC
0ms/2121KB

3

AC
0ms/2132KB

4

AC
8ms/2226KB

5

AC
8ms/2242KB

6

AC
8ms/2214KB

7

AC
8ms/2156KB

8

AC
168ms/6953KB

9

AC
160ms/6953KB

10

AC
160ms/6894KB

原文地址:https://www.cnblogs.com/erutsiom/p/9158918.html