SUOI #37 清点更多船只

经典问题
正常解法是树状数组或线段树

但这题内存给的很小
分块可以省内存

我们分析一下

树状数组线段树分块
时间$NlogN=20\times 10^6$$NlogN=20\times 10^6$$N\sqrt N=10^3\times 10^6$
内存$2\times N=2\times 8\times 10^6$$4\times N=4\times 24\times 10^6$$N+ \sqrt N=8024000$

然后发现都不可行
怎么办呢?

其实
线段树
没必要开全

我们可以开到长度为16就不开了
时间乘10^616
内存可以做到\(1.75\times N\)

#include <iostream>
#include <string>
//#include <fstream>

using namespace std;

//ifstream inf("stp.in", ios_base::in);
//ofstream outf("stp.out", ios_base::out);

const int MAXN=1111111;
const int Len=15;

string com;
int L, R;
long long op;
int N, M;
long long Num[MAXN];

struct Node{
    int r, l, ls, rs;
    long long sum, opt;
} T[MAXN>>3];
int Tcnt=0;

long long getsum(int l, int r){
    long long ret=0LL;
    for(int i=l;i<=r;++i)   ret+=Num[i];
    return ret;
}

void getup(int l, int r){
    for(int i=l;i<=r;++i)   Num[i]+=op;
}

void pup(int at){
    if(T[at].r-T[at].l<=Len)    T[at].sum=getsum(T[at].l, T[at].r);
    else    T[at].sum=T[T[at].ls].sum+T[T[at].rs].sum;
}

void BuildTree(int l, int r, int at){
    T[at].l=l;T[at].r=r;T[at].opt=0LL;
    T[at].ls=-1;T[at].rs=-1;
    if(r-l>Len){
        int m=(l+r)>>1;
        ++Tcnt;T[at].ls=Tcnt;
        BuildTree(l, m, Tcnt);
        ++Tcnt;T[at].rs=Tcnt;
        BuildTree(m+1, r, Tcnt);
    }
    pup(at);
}

void cop(int at){
    T[at].opt+=op;
}

void opr(int at){
    T[at].sum+=(long long)(T[at].r-T[at].l+1)*op;
}

void pdw(int at){
    if(T[at].opt==0LL)  return;
    int top=op;op=T[at].opt;
    if(T[at].r-T[at].l<=Len){
        getup(T[at].l, T[at].r);
    }
    else{
        opr(T[at].ls);cop(T[at].ls);
        opr(T[at].rs);cop(T[at].rs);
    }
    op=top;T[at].opt=0LL;
}

long long Ask(int at){
    if(T[at].l>=L && T[at].r<=R){
        return T[at].sum;
    }
    long long ret=0LL;
    pdw(at);
    if(T[at].r-T[at].l<=Len){
        ret=getsum(max(T[at].l, L), min(T[at].r, R));
//      for(int i=max(T[at].l, L);i<=min(T[at].r, R);++i)
//          ret+=Num[i];
    }
    else{
        int m=(T[at].l+T[at].r)>>1;
        if(L<=m)    ret+=Ask(T[at].ls);
        if(R>m) ret+=Ask(T[at].rs);
    }
    return ret;
}

void Update(int at){
    if(T[at].l>=L && T[at].r<=R){
        opr(at);cop(at);
        return;
    }
    pdw(at);
    if(T[at].r-T[at].l<=Len){
        getup(max(T[at].l, L), min(T[at].r, R));
//      for(int i=max(T[at].l, L);i<=min(T[at].r, R);++i)
//          Num[i]+=op;
    }
    else{
        int m=(T[at].l+T[at].r)>>1;
        if(L<=m)    Update(T[at].ls);
        if(R>m) Update(T[at].rs);
    }
    pup(at);
}

int main(){
    ios_base::sync_with_stdio(false);
    
    cin >> N >> M;
    for(int i=1;i<=N;++i)   cin >> Num[i];
    
    ++Tcnt;
    BuildTree(1, N, Tcnt);
    
    for(int i=1;i<=M;++i){
        cin >> com;
        if(com[1]=='s'){
            cin >> L >> R;
            cout << Ask(1) << endl;
        }
        else{
            cin >> L >> R >> op;
            Update(1);
        }
    }
    
    return 0;
}

转载于:https://www.cnblogs.com/Pickupwin/p/8613556.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值