hdu6315 线段树+树状数组 附模板

杭电多校第二场1007题

题意

给你两个大小为n的数组,a,b,初始b为1到n的一个全排列(注意是全排列!),a全为0。有q次操作。

每次操作有两种情况:

1. 将a数组区间[l,r]的每个数+1。 
2. 输出Σa[i]/b[i](l<=i<=r)Σa[i]/b[i](l<=i<=r)。

思路

在只有当 ai 加1的次数能够整除 bi 时才会对区间答案有贡献,这样用线段树记录一开始每个点初始化为bi,每次访问的区间有这个点就减1,当为0时表示可以整除bi此位置的贡献加1,然后向下查找初始的bi的值重新赋值,而当查询区间的和时,加上每个点的贡献就行了。

线段树用来维护最小值(初始值为b,多次查询区间最值,当区间最小值为0时更新树状数组),树状数组用来求ai/bi的和。

代码实现时主要就是需要树状数组和线段树的模板,然后还需要自己写一个dfs(用来搜素当某区间为0时该区间内0的值的位置)。

 

附上:

线段树模板  https://blog.csdn.net/leo_10/article/details/80875262       //下标从1开始

树状数组模板  https://blog.csdn.net/zars19/article/details/54620021   //下标从1开始

树状数组与线段树区别: 

树状数组的修改是增加某个数或者减少某个数,不适合替换成某个数,树状数组的查询只能是询问前i项的和

两者在复杂度上同级, 但是树状数组的常数明显优于线段树, 其编程复杂度也远小于线段树.
树状数组的作用被线段树完全涵盖, 凡是可以使用树状数组解决的问题, 使用线段树一定可以解决, 但是线段树能够解决的问题树状数组未必能够解决.
树状数组的突出特点是其编程的极端简洁性, 使用lowbit技术可以在很短的几步操作中完成树状数组的核心操作,与之相关的便是其代码效率远高于线段树。
另外,当问题推广到高维情形时高维树状数组有高维线段树所无法企及的常数优势。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define MAXN 100010
#define inf 0x3f3f3f3f
using namespace std;

int n,m;
int a[MAXN];//初始数组
int c[MAXN];//树状数组求和

//树状数组相关函数:
int lowbit(int x){
    return (-x)&x;
}
void add(int pos,int x){
    while(pos<=n){
        c[pos]+=x;
        pos+=lowbit(pos);
    }
}

int query1(int pos){
    int res=0;
    while(pos>0){
        res+=c[pos];
        pos-=lowbit(pos);
    }
    return res;
}

//线段树相关函数:
struct node{
    int l,r;//区间[l,r]
    int add;//区间的延时标记
    int mn; //区间最小值
}tree[MAXN<<2];//一定要开到4倍多的空间

void pushup(int index){
    tree[index].mn = min(tree[index<<1].mn,tree[index<<1|1].mn);
}
void pushdown(int index){
    //说明该区间之前更新过
    //要想更新该区间下面的子区间,就要把上次更新该区间的值向下更新
    if(tree[index].add){
        //在原来的值的基础上加上val
        tree[index<<1].mn += tree[index].add;
        tree[index<<1|1].mn += tree[index].add;
        tree[index<<1].add += tree[index].add;
        tree[index<<1|1].add += tree[index].add;
        tree[index].add = 0;

    }
}

void build(int l,int r,int index){
    tree[index].l = l;
    tree[index].r = r;
    tree[index].add = 0;//刚开始一定要清0
    if(l == r){
        scanf("%d",&tree[index].mn);
        tree[index].mn=a[smark++];
        return ;
    }
    int mid = (l+r)>>1;
    build(l,mid,index<<1);
    build(mid+1,r,index<<1|1);
    pushup(index);
}

void dfs(int i){
    if(tree[i].l==tree[i].r){
        add(tree[i].l,1);
        tree[i].mn=a[tree[i].l];
        return;
    }
    int ls=i*2,rs=i*2+1;
    if(tree[ls].mn==0)//更新左孩子
    {
        pushdown(ls);
        dfs(ls);
    }
    if(tree[rs].mn==0)//更新右孩子
    {
        pushdown(rs);//更新懒标记
        dfs(rs);
    }
    pushup(i);
}

void updata(int l,int r,int index,int val){
    if(l <= tree[index].l && r >= tree[index].r){
        /*把原来的值替换成val,因为该区间有tree[index].r-tree[index].l+1
        个数,所以区间和 以及 最值为:
        */
        /*tree[index].sum = (tree[index].r-tree[index].l+1)*val;
        tree[index].mn = val;
        tree[index].mx = val;
        tree[index].add = val;//延时标记*/
        //在原来的值的基础上加上val,因为该区间有tree[index].r-tree[index].l+1
        //个数,所以区间和 以及 最值为:
        tree[index].mn += val;
        tree[index].add += val;//延时标记

        while(tree[index].mn==0){//*新加
            pushdown(index);
            dfs(index);
        }

        return ;
    }
    pushdown(index);
    int mid = (tree[index].l+tree[index].r)>>1;
    if(l <= mid){
        updata(l,r,index<<1,val);
    }
    if(r > mid){
        updata(l,r,index<<1|1,val);
    }
    pushup(index);
}
int query(int l,int r,int index){
    if(l <= tree[index].l && r >= tree[index].r){
        return tree[index].mn;
    }
    pushdown(index);
    int mid = (tree[index].l+tree[index].r)>>1;
    int Min = inf;
    if(l <= mid){
        Min = min(query(l,r,index<<1),Min);
    }
    if(r > mid){
        Min = min(query(l,r,index<<1|1),Min);
    }
    return Min;
}
int main(){
    int x,y,z;
    char q[10];
    while(~scanf("%d%d",&n,&m)){
        smark=1;
        for(int i=1;i<=n;i++)
            cin>>a[i];
        build(1,n,1);
        while(m--){
            scanf("%s",q);
            if(q[0]=='q'){
                scanf("%d %d",&x,&y);
                cout<<(query1(y)-query1(x-1))<<endl;
            }
            else{
                scanf("%d %d",&x,&y);
                updata(x,y,1,-1);
                //for(int i = 1; i <= n; ++i)
                    //printf("a[%d] = %d\n",i,query(i,i,1));
            }
        }
    }
    return 0;
}

附:线段树模板

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define MAXN 100010
#define inf 0x3f3f3f3f

using namespace std;

struct node{
    int l,r;//区间[l,r]
    int add;//区间的延时标记
    int sum;//区间和
    int mx; //区间最大值
    int mn; //区间最小值
}tree[MAXN<<2];//一定要开到4倍多的空间

void pushup(int i){
    tree[i].sum = tree[i<<1].sum+tree[i<<1|1].sum;
    tree[i].mx = max(tree[i<<1].mx,tree[i<<1|1].mx);
    tree[i].mn = min(tree[i<<1].mn,tree[i<<1|1].mn);
}
void pushdown(int i){
    //说明该区间之前更新过
    //要想更新该区间下面的子区间,就要把上次更新该区间的值向下更新
    if(tree[i].add){
        //替换原来的值
        /*
        tree[i<<1].sum = (tree[i<<1].r-tree[i<<1].l+1)*tree[i].add;
        tree[i<<1|1].sum = (tree[i<<1|1].r-tree[i<<1|1].l+1)*tree[i].add;
        tree[i<<1].mx = tree[i].add;
        tree[i<<1|1].mx = tree[i].add;
        tree[i<<1].mn = tree[i].add;
        tree[i<<1|1].mn = tree[i].add;
        tree[i<<1].add = tree[i].add;
        tree[i<<1|1].add = tree[i].add;
        tree[i].add = 0;*/
        //在原来的值的基础上加上val

        tree[i<<1].sum += (tree[i<<1].r-tree[i<<1].l+1)*tree[i].add;
        tree[i<<1|1].sum +=(tree[i<<1|1].r-tree[i<<1|1].l+1)*tree[i].add;
        tree[i<<1].mx += tree[i].add;
        tree[i<<1|1].mx += tree[i].add;
        tree[i<<1].mn += tree[i].add;
        tree[i<<1|1].mn += tree[i].add;
        tree[i<<1].add += tree[i].add;
        tree[i<<1|1].add += tree[i].add;
        tree[i].add = 0;

    }
}
void build(int l,int r,int i){
    tree[i].l = l;
    tree[i].r = r;
    tree[i].add = 0;//刚开始一定要清0
    if(l == r){
        scanf("%d",&tree[i].sum);
        tree[i].mn = tree[i].mx = tree[i].sum;
        return ;
    }
    int mid = (l+r)>>1;
    build(l,mid,i<<1);
    build(mid+1,r,i<<1|1);
    pushup(i);
}
void updata(int l,int r,int i,int val){
    if(l <= tree[i].l && r >= tree[i].r){
        /*把原来的值替换成val,因为该区间有tree[i].r-tree[i].l+1
        个数,所以区间和 以及 最值为:
        */
        /*tree[i].sum = (tree[i].r-tree[i].l+1)*val;
        tree[i].mn = val;
        tree[i].mx = val;
        tree[i].add = val;//延时标记*/
        //在原来的值的基础上加上val,因为该区间有tree[i].r-tree[i].l+1
        //个数,所以区间和 以及 最值为:
        tree[i].sum += (tree[i].r-tree[i].l+1)*val;
        tree[i].mn += val;
        tree[i].mx += val;
        tree[i].add += val;//延时标记

        return ;
    }
    pushdown(i);
    int mid = (tree[i].l+tree[i].r)>>1;
    if(l <= mid){
        updata(l,r,i<<1,val);
    }
    if(r > mid){
        updata(l,r,i<<1|1,val);
    }
    pushup(i);
}
int query(int l,int r,int i){
    if(l <= tree[i].l && r >= tree[i].r){
        //return tree[i].sum;
        return tree[i].mx;
        //return tree[i].mn;
    }
    pushdown(i);
    int mid = (tree[i].l+tree[i].r)>>1;
    int ans = 0;
    int Max = 0;
    int Min = inf;
    if(l <= mid){
        ans += query(l,r,i<<1);
        Max = max(query(l,r,i<<1),Max);
        Min = min(query(l,r,i<<1),Min);
    }
    if(r > mid){
        ans += query(l,r,i<<1|1);
        Max = max(query(l,r,i<<1|1),Max);
        Min = min(query(l,r,i<<1|1),Min);
    }
    //return ans;
    return Max;
    //return Min;
}
int main()
{
    int n,m,q,x,y,z;
    while(~scanf("%d%d",&n,&m)){
        build(1,n,1);
        while(m--){
            scanf("%d",&q);
            if(q == 1){
                //cout<<"查询:(x,y)"<<endl;
                scanf("%d %d",&x,&y);
                cout<<query(x,y,1)<<endl;
            }
            else{
                //cout<<"更新(x,y)为z:"<<endl;
                scanf("%d %d %d",&x,&y,&z);
                updata(x,y,1,z);
                /*for(int i = 1; i <= n; ++i){ //单点输出
                    printf("a[%d] = %d\n",i,query(i,i,1));
                }*/
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值