线段树(模板题)

24 篇文章 0 订阅
23 篇文章 0 订阅

链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1547

思想

对于一条数链,二分,然后二分,然后再二分…

【题目描述】

给定一数列,规定有两种操作,一是修改某个元素,二是求区间的连续和。(数列元素初始化为0)

【输入】

输入数据第一行包含两个正整数n,m(n≤100000,m≤500000),以下是m行,

每行有三个正整数k,a,b(k=0或1,a,b≤n).k=0时表示将a处数字加上b,k=1时表示询问 区间[a,b]内所有数的和。

【输出】

对于每个询问输出对应的答案。

【输入样例】
10 20
0 1 10
1 1 4
0 6 6
1 4 10
1 8 9
1 4 9
0 10 2
1 1 8
0 2 10
1 3 9
0 7 8
0 3 10
0 1 1
1 3 8
1 6 9
0 5 5
1 1 8
0 4 2
1 2 8
0 1 1
【输出样例】
10
6
0
6
16
6
24
14
50
41
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100005;
int i,j,k;
int read()
{
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-f;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		s=(s<<3)+(s<<1)+ch-'0';
	}
	return s*f;
}
int n,m;long long c[N*4],a[N];
void update(int k,int l,int r,int x,int y)//在a[l]~a[r]查找a[x]并将其值加上y,再更新其所在线段树的值 
{
	if(r<x||l>x) return;
	if(l=r&&l==x){
		c[k]+=y;return;
	}
	int mid=(l+r)>>1;
	update(k*2,l,mid,x,y);
	update(k*2+1,mid+1,r,x,y);
	c[k]=c[k*2]+c[k*2+1];
}
long long query(int k,int l,int r,int x,int y)//在区间a[l]~a[r]中查找a[x]~a[y]的区间和
{
	if(l>y||r<x)return 0;
	if(l>=x&&r<=y) return c[k];
	int mid=(l+r)>>1;
	long long ans=0;
	ans+=query(k*2,l,mid,x,y);
	ans+=query(k*2+1,mid+1,r,x,y);	
	return ans; 
} 

int main()
{
	n=read(),m=read();
	for(i=1;i<=m;i++)
	{
		int k1=read(),l=read(),r=read();
		if(k1==0)
		update(1,1,n,l,r);
		else
		printf("%lld\n",query(1,1,n,l,r));
	}
	return 0;
}

附LDX大佬的板子链接:https://blog.csdn.net/dreaming__ldx/article/details/81008642
区间修改区间求和

描述

如题,已知一个数列,你需要进行下面两种操作:(初始化不为0)

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

输出

输出包含若干行整数,即为所有操作2的结果。

样例输入 [复制]

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

样例输出 [复制]

11
8
20

提示

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

保证在int64/long long数据范围内

#include<iostream>//标程
#include<cstdio>
#define MAXN 1000001
#define ll long long
using namespace std;
unsigned ll n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];
inline ll ls(ll x)
{
    return x<<1;
}
inline ll rs(ll x)
{
    return x<<1|1;
}
void scan()
{
    cin>>n>>m;
    for(ll i=1;i<=n;i++)
    scanf("%lld",&a[i]);
}
inline void push_up(ll p)
{
    ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(ll p,ll l,ll r)
{
    tag[p]=0;
    if(l==r){ans[p]=a[l];return ;}
    ll mid=(l+r)>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    push_up(p);
} 
inline void f(ll p,ll l,ll r,ll k)
{
    tag[p]=tag[p]+k;
    ans[p]=ans[p]+k*(r-l+1);
}
inline void push_down(ll p,ll l,ll r)
{
    ll mid=(l+r)>>1;
    f(ls(p),l,mid,tag[p]);
    f(rs(p),mid+1,r,tag[p]);
    tag[p]=0;
}
inline void update(ll nl,ll nr,ll l,ll r,ll p,ll k)
{
    if(nl<=l&&r<=nr)
    {
        ans[p]+=k*(r-l+1);
        tag[p]+=k;
        return ;
    }
    push_down(p,l,r);
    ll mid=(l+r)>>1;
    if(nl<=mid)update(nl,nr,l,mid,ls(p),k);
    if(nr>mid) update(nl,nr,mid+1,r,rs(p),k);
    push_up(p);
}
ll query(ll q_x,ll q_y,ll l,ll r,ll p)
{
    ll res=0;
    if(q_x<=l&&r<=q_y)return ans[p];
    ll mid=(l+r)>>1;
    push_down(p,l,r);
    if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));
    if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p));
    return res;
}
int main()
{
    ll a1,b,c,d,e,f;
    scan();
    build(1,1,n);
    while(m--)
    {
        scanf("%lld",&a1);
        switch(a1)
        {
            case 1:{
                scanf("%lld%lld%lld",&b,&c,&d);
                update(b,c,1,n,1,d);
                break;
            }
            case 2:{
                scanf("%lld%lld",&e,&f);
                printf("%lld\n",query(e,f,1,n,1));
                break;
            }
        }
    }
    return 0;
}
#include<bits/stdc++.h>//修正 
using namespace std;
#define mid (T[p].l+T[p].r>>1)
const int N=1e5+5;
struct fjy{
    long long l,r,lz,sum;
}T[N<<2];//线段树大小开4*n 
long long a[N];
inline long long lc(long long x)
{
    return x<<1;
}
inline long long rc(long long x)
{
    return x<<1|1;
}
long long read1(){
    long long s=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') f=-f;
        ch=getchar();
    }
    while(isdigit(ch)){
        s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*f;
}
long long read2(){
    long long s=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-f;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<3)+(s<<1)+ch-'0';
        ch=getchar();
    }
    return s*f;
}
void pushup(int p){
    T[p].sum=T[lc(p)].sum+T[rc(p)].sum;
}
void pushnow(int p,long long v){
    T[p].sum+=(T[p].r-T[p].l+1)*v;
    T[p].lz+=v;
}
void pushdown(int p){//下传p的懒惰标记 
    pushnow(lc(p),T[p].lz);
    pushnow(rc(p),T[p].lz);
    T[p].lz=0;
}
void build(int p,int l,int r){
    T[p].l=l,T[p].r=r,T[p].sum=0;
    if(l==r) {T[p].sum=a[l];	return;}
    build(lc(p),l,mid),build(rc(p),mid+1,r);
    T[p].sum=T[lc(p)].sum+T[rc(p)].sum;
    
}
void update(int p,int ql,int qr, long long v){
    if(T[p].l>qr||T[p].r<ql) {/*printf("%dfalse ",p);*/	return;}
    if(T[p].l>=ql&&T[p].r<=qr) {T[p].sum+=(T[p].r-T[p].l+1)*v,T[p].lz+=v;return;/*记得要返回*/} 
    pushdown(p);
    if(ql<=mid) update(lc(p),ql,qr,v);
    if(qr>mid) update(rc(p),ql,qr,v);
    pushup(p);
}
long long query(int p,int ql,int qr){
    if(T[p].l>qr||T[p].r<ql) return 0;
    if(T[p].l>=ql&&T[p].r<=qr) return T[p].sum;
    pushdown(p);
    long long ant=0;
    if(ql<=mid) ant+=query(lc(p),ql,qr);
    if(qr>mid) ant+=query(rc(p),ql,qr);
    return ant;
}

int n,m,juge,k,x,y;
int main(){
    n=read1(),m=read1();
    for(int i=1;i<=n;i++) a[i]=read1();
    build(1,1,n);                                    // for(int i=1;i<=n*2-1;i++) printf("%d;",T[i].sum);printf("\n");
    for(int i=1;i<=m;i++){
        juge=read1(),x=read1(),y=read1();
        if(juge==1){
            k=read1();
            update(1,x,y,k);                           //for(int i=1;i<=n*2-1;i++) printf("%d;",T[i].sum);printf("\n");
        }
        else
        printf("%lld\n",query(1,x,y));
    }
    return 0;
}

错误总结:

  • 数据读入后记得建树(这是血的教训
  • update函数中,若需更新区间不能完全覆盖目标区间,要将目标区间的懒惰标记下传给儿子,在访问子区间(这也是前人的枯骨总结出的
  • update函数中,若需更新区间能完全覆盖目标区间,更新目标区间的sum和lz的值后需要返回
  • 线段树的大小要开成n*4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值