HDU-1166 敌兵布阵 (基础线段树)

题意:给N个数,然后给出N个数对应的数值,然后对叶子结点进行add,sub,query操作 输入end时即寻问结束

思路:对于RMQ问题当然使用线段树比较方便,同时这道题也是最简单的线段树(因为只对叶子结点进行修改,而没有对区间进行修改)对结点进行sub(减)操作即使 加入一个相反数

 然后这道题主要是 对建树(build) ,结点更新(update),以及区间查询(query) ,由于没有对区间进行修改所以不需要使用push_down().

建树:开空间是maxn(最大结点数的四倍):就是计算整个满二叉树的结点数,log2N为高度 ,从底层往上每次结点数减半(等比数列) 所以最后利用等比数列公式推导近似等于4N

然后就是递归建树:即使将总区间一直二分减小,直到不能减小时即找到了叶子结点位置

更新:也是同理,递归找到结点修改,然后再push_up()向上更新整棵树

完整题解:

//对于线段树来说某人1~mid/2,mid/2+1~n为一个区间 
#include <iostream>
#define rep(i,n) for(int i=1;i<=n;i++)
//( 0<N<=200000,0<M<5000 )
using namespace std;
const  int maxn = 2e5+1;
int seg[maxn<<2];
int arr[maxn];
inline int ls(int x){
    return x<<1;
}
inline int rs(int x){
    return x<<1|1;
}
inline void push_up(int p)
{
    seg[p]=seg[ls(p)]+seg[rs(p)];
}
//基础建树 
void build(int p,int l,int r){//p为下标 
    if(l==r) {
        seg[p] = arr[l];//左右相等时即意味着找到了结点位置 
        return ;
    }
    int mid = (l+r)>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    push_up(p);//将p点向上更新结点 
}
//对于单点更新来说就是找到更新点坐标然后去push_up(向上更新) 
//也就是要先找到点坐标 p为下标,k为修改的值,q为要增加的大小 
void update(int k,int p,int l,int r,int q){
    if(k==seg[p]&&l==r) {
        seg[p] += q;
        return;
    }
    int mid = (l+r)>>1; 
    if(mid<k) update(k,rs(p),mid+1,r,q);//递归寻找 
    else update(k,ls(p),l,mid,q);
    push_up(p);
} 
int query(int qx,int qy,int l,int r,int p){
    int res = 0;
    if(qx<=l&&r<=qy) return seg[p];//即寻找到该区间
    int mid = (l+r)>>1;
    if(qx<=mid) res+=query(qx,qy,l,mid,ls(p));
    if(qy>mid) res+=query(qx,qy,mid+1,r,rs(p));
    return res; 
}
int main(){
    int T,n;
    int cnt =0;
    scanf("%d",&T);
    while(T--){
        int a1,b,c,d,e,f;
        cin>>n;
        rep(i,n){
            cin>>arr[i];
        }        
        build(1,1,n);
        string s; 
        cout<<"Case "<<++cnt<<":"<<endl;
        while(cin>>s){
            if(s=="End") break;
            else {
                if(s=="Query"){
                       scanf("%d%d",&e,&f);
                    printf("%d\n",query(e,f,1,n,1));
                }else if(s=="Add"){
                    scanf("%d%d",&b,&d);//在b点加d 
                    update(b,1,1,n,d);
                }else{
                    scanf("%d%d",&b,&d);//区间减法相当于加法的相反数 
                    update(b,1,1,n,-d);
                }    
            } 
        } 
    }
}

 

 

 

别人的模板修改:(这个有区间修改)

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const int MAXN = 5e5+1;
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;//1 
    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(){
    int T;
    int cnt =0;
    scanf("%d",&T);
    while(T--){
        ll a1,b,c,d,e,f;
        scan();
        build(1,1,n);
        string s; 
        cout<<"Case "<<++cnt<<":"<<endl;
        while(cin>>s){
            if(s=="End") break;
            else {
                if(s=="Query"){
                       scanf("%lld%lld",&e,&f);
                    printf("%lld\n",query(e,f,1,n,1));
                }else if(s=="Add"){
                    scanf("%lld%lld",&b,&d);//1 x y k 含义:将区间[x,y]内每个数加上k
                    update(b,b,1,n,1,d);
                }else{
                    scanf("%lld%lld",&b,&d);//区间减法相当于加法的相反数 
                    update(b,b,1,n,1,-d);
                }    
            } 
        } 
    }
}

 

转载于:https://www.cnblogs.com/Tianwell/p/11236892.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值