线段树模板+例题

20 篇文章 0 订阅

例题: 敌兵布阵(改点求点)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int mod=1e9+10;
const int M=5e4 + 10;
ll a[M*4]={0};
ll tree[M*4]={0};
ll Add[M*4]={0};
void bulid(ll node, ll left, ll right)//建树
{
    if(left == right){
        tree[node] = a[left];
        return ;
    }
    ll mid = (left + right) / 2;
    bulid(node * 2, left, mid);
    bulid(node * 2 + 1, mid +1, right);
    tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
void PushDown(ll node, ll ln, ll rn)//左右子树叶节点的数量
{
    if(Add[node]){
        Add[node*2] += Add[node];
        Add[node*2+1] += Add[node];
        tree[node*2] += Add[node] * ln;
        tree[node*2 + 1] += Add[node] * rn;
        Add[node] = 0;
    }
    return ;
}
ll query(ll node, ll L, ll R, ll left, ll right)//区间查询
{
    if(L <= left && right <= R)
        return tree[node];
    ll mid = (left + right)/2;
    PushDown(node, mid- left + 1, right - mid);
    ll ans = 0;
    if(L <= mid )
        ans  = query(node * 2, L, R, left, mid);
    if(R > mid)
        ans += query(node * 2 + 1, L, R, mid+1, right);
    return ans;
}/*
void update1(ll node, ll left, ll right, ll idx, ll val)//单点更新
{
    if(left == right){
        //a[idx] += val;
        tree[node] += val;
        return ;
    }
    ll mid = (left + right) / 2;
    if(idx >= left && idx <=mid)
        update1(node*2, left, mid, idx, val);
    else
        update1(node * 2 +1, mid + 1, right, idx, val);
    tree[node] = tree[node * 2] + tree[node * 2 + 1];
}*/
//区间更新 L~R 加 P
void update2(ll node, ll left, ll right, ll L, ll R, ll p)
{
    if(left >= L && right <= R){
        tree[node] += p * (right - left +1);
        Add[node] += p;
        return ;
    }
    ll mid = (left + right)/2;
    if(L <= mid)
        update2(node*2, left, mid, L, R, p);
    else
        update2(node*2+1, mid+1, right, L, R, p);
}
int main()
{
    ll n,i,j,T,aa,bb,e=1;
    scanf("%lld",&T);
    while(T--){
        memset(Add,0,sizeof(Add));
        memset(tree,0,sizeof(tree));
        memset(a,0,sizeof(a));
        scanf("%lld",&n);
        for(int i=1; i<=n; i++)
            scanf("%lld",&a[i]);
        bulid(1, 1, n);
        char s[10];
        printf("Case %lld:\n",e++);
        while(1){
            //cin>>s;
            scanf(" %s",s);
            if(s[0] == 'E')
                break;
            scanf("%lld %lld",&aa, &bb);
            if(s[0] == 'Q'){
                ll ans = query(1, aa, bb, 1, n);
                printf("%lld\n",ans);
            }
            else if(s[0] == 'A'){
                update1(1, 1, n, aa, bb);
            }
            else if(s[0] == 'S'){
                update1(1, 1, n, aa, -bb);
            }
        }
    }
    return 0;
}

//求区间最大值时更改tree就可以了,取子节点的最大值

Color the ball(该段求点)

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
const int MAX=100100;
int a[MAX];
int sum[4*MAX];
int add[4*MAX];
/*void Build(int l,int r,int n)//建立线段树
{
    if(l==r)//到叶子结点赋值
    {
        sum[n]=a[l];
        return ;
    }
    int m=(l+r)/2;
    Build(l,m,2*n);//建立左子树
    Build(m+1,r,2*n+1);//建立右子树
    sum[n]=sum[2*n]+sum[2*n+1];
    return ;
}*/
void PushDown(int n,int ln,int rn)//ln和rn是左子树和右子树树叶的数量
{
    if(add[n])
    {
        add[n*2]+=add[n];
        add[n*2+1]+=add[n];

        sum[n*2]+=add[n]*ln;
        sum[n*2+1]+=add[n]*rn;
        add[n]=0;
    }
    return ;
}
void Update2(int L,int R,int c,int l,int r,int n)
{
    if(l>=L&&R>=r)
    {
        sum[n]+=c*(r-l+1);
        add[n]+=c;
        return ;
    }
    int m=(l+r)/2;
    PushDown(n,m-l+1,r-m);
    if(L<=m)
        Update2(L,R,c,l,m,n*2);
    if(R>=m+1)
        Update2(L,R,c,m+1,r,n*2+1);
    sum[n]=sum[2*n]+sum[2*n+1];
    return ;
}
/*
void Update(int l,int r,int sumn,int am,int p)//p是更改的值
{
    //sumn是sum数组里面的,am是a数组里面的
    //首先要找到需要更新的am的位置
    int mid;
    mid=(l+r)/2;
    if(l==r)
    {
        sum[sumn]+=p;
        return ;
    }
    if(mid>=am)
    {
        Update(l,mid,sumn*2,am,p);
    }
    else
    {
        Update(mid+1,r,sumn*2+1,am,p);
    }
    //在回来的路上顺便更新sum数组。
    sum[sumn]=sum[sumn*2]+sum[sumn*2+1];
}*/
int Query(int L,int R,int l,int r,int p)//p是当前位置
{
    if(L<=l&&r<=R)
        return sum[p];
    int m=(l+r)/2;

    PushDown(p,m-l+1,r-m);

    int ans=0;
    if(L<=m)
        ans+=Query(L,R,l,m,p*2);
    if(R>m)
        ans+=Query(L,R,m+1,r,p*2+1);
    return ans;
}
int main()
{
    int n,i;
    while(cin>>n&&n)
    {
        memset(a,0,sizeof(a));
        memset(sum,0,sizeof(sum));
        memset(add,0,sizeof(add));
        for(i=0;i<n;i++)
        {
            int x,y;
            cin>>x>>y;
            Update2(x,y,1,1,n,1);
        }
        for(i=1;i<=n;i++)
        {
            if(i==1)
                cout<<Query(i,i,1,n,1);
            else
                cout<<" "<<Query(i,i,1,n,1);
        }
        cout<<endl;
    }
    return 0;
}
     

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值