HDU-1166 敌兵布阵(分块/树状数组/线段树)

题意

给定 n 个数,有三种操作:给某个数加一个值,给某个数减一个值,询问一段区间数的总和(万能模板题)。
1 n 50000

思路

练习数据结构的模板题,有三种比较基本的数据结构可以解决,分块、树状数组、线段树,这里逐一介绍。

分块

在我的理解中,分块并不是一种数据结构,而是一种思想。对一段连续的区间赋值,就当是 n 吧,复杂度是 O ( n ) ,但如果将这个 n 分成若干块,每块大小是 S (可以不完整),平均下来有是 n S 块。每次修改碰到完整区间可以先打上标记,等下次需要查询这个区间部分的时候再解开标记向内部更新(用这次的复杂度完成上次的事情,延迟更新的思想)。这样一次修改、查询就都变成了 O ( S + n S ) ,而基本不等式告诉我们,当 S n 时,能保证复杂度尽可能小,是 O ( n ) 级别的。

树状数组

树状数组,我一般将它看作一种特殊的前缀数组,通过递归(写成循环)得到结果。设 a 的树状数组为 c ,那么 c i l o w b i t ( i ) 个元素,如 c 6 有两个元素, c 7 有一个元素。如在求 a 1 + a 2 + . . . + a 6 时,先访问 c 6 ,这样就得到了后 l o w b i t ( 6 ) 即后两个元素的总和,然后原式就转化成了求 a 1 + a 2 + . . . + a 4 的和。也就是求 a 1 + a 2 + . . . + a n 实际上是将 n 二进制位上的 1 从低到高逐步剥去。而给某个元素加上某个值,只是上述的逆运算罢了,不断加目前加到数 x l o w b i t ( x ) 。这样也不难看出,复杂度都是 O ( l o g n ) 级别的,而且常数非常小,一般不到 O ( l o g n ) 次就完成了。

线段树

线段树,节点表示的是一段线段(区间)的树,无论是区间(单点)修改、区间(单点)查询,复杂度都能保证在 O ( l o g n ) 级别,但常数比树状数组大。线段树我认为还是分治的思想体现的更到位。在要对区间 [ L , R ] 查询或修改时,如果目前递归到的节点是 [ l , r ] 我们只用取 [ l , r ] 的中点 m i d ,如果 [ L , R ] 完全在 m i d 的一边,那就只往那一边递归,否则向两边递归。线段树的区间修改也有分块的延迟更新思想在,比如当完整覆盖某个区间时,可以先对这个区间打上标记,等下次需要部分查询时再解开标记。

代码

分块
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 50003
typedef long long LL;
using namespace std;

template<const int maxn>struct Block
{
    int a[maxn],sum[maxn],tag[maxn];
    int bs,cnt;
    Block(){bs=sqrt(maxn),cnt=(maxn+bs-1)/bs;}
    void build(int *arr,int n)
    {
        memset(sum,0,sizeof(sum));
        memset(tag,0,sizeof(tag));
        FOR(i,1,n)
        {
            a[i]=arr[i];
            sum[i/bs]+=a[i];
        }
    }
    void push_down(int _)
    {
        if(!tag)return;
        FOR(i,_*bs,(_+1)*bs-1)
            a[i]+=tag[_];
        tag[_]=0;
    }
    void update(int x,int y,int addval)
    {
        int kx=x/bs,ky=y/bs;
        push_down(kx),push_down(ky);
        if(kx==ky)
        {
            FOR(i,x,y)
            {
                a[i]+=addval;
                sum[kx]+=addval;
            }
            return;
        }
        for(int i=x;i<(kx+1)*bs;i++)
        {
            a[i]+=addval;
            sum[kx]+=addval;
        }
        for(int i=ky*bs;i<=y;i++)
        {
            a[i]+=addval;
            sum[ky]+=addval;
        }
        FOR(i,kx+1,ky-1)
        {
            tag[i]+=addval;
            sum[i]+=bs*addval;  
        }
    }
    int query(int x,int y)
    {
        int res=0,kx=x/bs,ky=y/bs;
        push_down(kx),push_down(ky);
        if(kx==ky)
        {
            FOR(i,x,y)res+=a[i];
            return res;
        }
        for(int i=x;i<(kx+1)*bs;i++)
            res+=a[i];
        for(int i=ky*bs;i<=y;i++)
            res+=a[i];
        FOR(i,kx+1,ky-1)
            res+=sum[i];
        return res;
    }
};
Block<N>B;
int a[N];
int main()
{
    int T,n;
    scanf("%d",&T);
    FOR(Ti,1,T)
    {
        scanf("%d",&n);
        FOR(i,1,n)scanf("%d",&a[i]);
        printf("Case %d:\n",Ti);
        B.build(a,n);
        char str[10];
        while(scanf("%s",str))
        {
            int x,y;
            if(str[0]=='A'||str[0]=='S')
            {
                scanf("%d%d",&x,&y);
                B.update(x,x,str[0]=='A'?y:-y);
            }
            else if(str[0]=='Q')
            {
                scanf("%d%d",&x,&y);
                printf("%d\n",B.query(x,y));
            }
            else if(str[0]=='E')break;
        }
    }
    return 0;
}
树状数组
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 50003
typedef long long LL;
using namespace std;
int sum[N],n;

void update(int x,int y)
{
    while(x<=n)
    {
        sum[x]+=y;
        x+=lowbit(x);
    }
    return;
}
int query(int x)
{
    int res=0;
    while(x)
    {
        res+=sum[x];
        x^=lowbit(x);
    }
    return res;
}

int main()
{
    int T,a;
    scanf("%d",&T);
    FOR(Ti,1,T)
    {
        memset(sum,0,sizeof(sum));
        scanf("%d",&n);
        printf("Case %d:\n",Ti);
        FOR(i,1,n)
        {
            scanf("%d",&a);
            update(i,a);
        }
        int x,y;
        char str[10];
        while(~scanf("%s",str))
        {
            if(str[0]=='Q')
            {
                scanf("%d%d",&x,&y);
                printf("%d\n",query(y)-query(x-1));
            }
            else if(str[0]=='A'||str[0]=='S')
            {
                scanf("%d%d",&x,&y);
                update(x,str[0]=='A'?y:-y);
            }
            else if(str[0]=='E')break;
        }
    }
    return 0;
}
线段树
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 50003
typedef long long LL;
using namespace std;

struct node
{
    int L,R,sum;
    int lth(){return R-L+1;}
};
struct SegmentTree
{
    node nd[N<<2];
    void build(int k,int L,int R,int *arr)
    {
        nd[k].L=L,nd[k].R=R;
        if(L==R)
        {
            nd[k].sum=arr[L];
            return;
        }
        build(k<<1,L,L+R>>1,arr);
        build(k<<1|1,(L+R>>1)+1,R,arr);
        nd[k].sum=nd[k<<1].sum+nd[k<<1|1].sum;
    }
    void update(int k,int x,int addval)
    {
        if(nd[k].L==nd[k].R)
        {
            nd[k].sum+=addval;
            return;
        }
        int mid=nd[k].L+nd[k].R>>1;
        if(x<=mid)update(k<<1,x,addval);
        if(mid<x)update(k<<1|1,x,addval);
        nd[k].sum=nd[k<<1].sum+nd[k<<1|1].sum;
        return;
    }
    int query(int k,int L,int R)
    {
        if(L<=nd[k].L&&nd[k].R<=R)return nd[k].sum;
        int res=0,mid=nd[k].L+nd[k].R>>1;
        if(L<=mid)res+=query(k<<1,L,R);
        if(mid<R)res+=query(k<<1|1,L,R);
        return res;
    }
}ST;
int a[N];

int main()
{
    int T,n;
    scanf("%d",&T);
    FOR(Ti,1,T)
    {
        scanf("%d",&n);
        FOR(i,1,n)scanf("%d",&a[i]);
        printf("Case %d:\n",Ti);
        ST.build(1,1,n,a);
        char str[10];
        while(~scanf("%s",str))
        {
            if(str[0]=='E')break;
            int x,y;
            scanf("%d%d",&x,&y);
            if(str[0]=='Q')printf("%d\n",ST.query(1,x,y));
            else if(str[0]=='A')ST.update(1,x,y);
            else if(str[0]=='S')ST.update(1,x,-y);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Paulliant/article/details/81186184
今日推荐