poj3468 zkw線段樹

這裏使用了zkw線段樹;
使標記永久化,並維護sum數組:僅考慮子樹的區間和;
這樣一個節點對答案的貢獻就是子樹和加上上方祖先的標記和;
代碼中L,R數組均可省去(但由於筆者太懶。。。。。。)
這樣就給出了一個與論文中不太一樣的sb方法,但各種複雜度還是一樣的;

#include<cstdio>
#define rep(i,k,n) for(int i=k;i<=n;i++)
#define rep2(i,k,n) for(int i=k;i>=n;i--)
using namespace std;
typedef long long ll;
void sc(ll& x){ll f=1;x=0;
    char c=getchar();while(c>'9' || c<'0'){if(c=='-')f=-f;c=getchar();}
    for(;c>='0' && c<='9';c=getchar())x=x*10+c-'0';
    x*=f;
}
void sc(int& x){int f=1;x=0;
    char c=getchar();while(c>'9' || c<'0'){if(c=='-')f=-f;c=getchar();}
    for(;c>='0' && c<='9';c=getchar())x=x*10+c-'0';
    x*=f;
}
typedef long long ll;
const int N=2e5+7;
ll v[N],sum[N<<1],dt[N<<1],L[N<<1],R[N<<1];
int M,n,m;
void build(){
    for(M=1;M<=n;M<<=1);
    rep(i,1,n)sum[M+i]=v[i];
    rep(i,1,M)L[M+i]=R[M+i]=i;
    rep2(i,M-1,1)sum[i]=sum[i<<1]+sum[i<<1|1],L[i]=L[i<<1],R[i]=R[i<<1|1];
}
inline void up(int x){if(x)sum[x]=sum[x<<1]+sum[x<<1|1]+(R[x]-L[x]+1)*dt[x];}
void Add(int s,int t,ll a){
    if(s>t)return;
    for(s=s+M-1,t=t+M+1;s^t^1;s>>=1,t>>=1){
        if(~s&1)dt[s^1]+=a,sum[s^1]+=(R[s^1]-L[s^1]+1)*a;
        if(t&1)dt[t^1]+=a,sum[t^1]+=(R[t^1]-L[t^1]+1)*a;
        up(s>>1),up(t>>1);
    }for(;s;s>>=1)up(s);
}
ll query(int s,int t){int l=s,r=t;
if(s>t)return 0;
    ll res=0;
    for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){
        if(~s&1)res+=sum[s^1];
        if(t&1)res+=sum[t^1];
        if(l<=R[s>>1])res+=(R[s>>1]-l+1)*dt[s>>1];
        if(r>=L[t>>1])res+=(r-L[t>>1]+1)*dt[t>>1];
    }
    for(s>>=1;s;s>>=1)res+=(r-l+1)*dt[s];
    return res;
}int main(){
    sc(n),sc(m);
    rep(i,1,n)sc(v[i]);
    build();
    char s[20];int l,r;ll a;
    rep(i,1,m){
        scanf("%s",s);
        if(s[0]=='C'){sc(l),sc(r),sc(a);Add(l,r,a);}
        else {sc(l),sc(r);printf("%I64d\n",query(l,r));
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章