codevs 1082 线段树练习 3 --分块练习

时间限制: 3 s
 空间限制: 128000 KB
 题目等级 : 大师 Master
题目描述 Description

给你N个数,有两种操作:


1:给区间[a,b]的所有数增加X


2:询问区间[a,b]的数的和。

输入描述 Input Description

第一行一个正整数n,接下来n行n个整数,

 

再接下来一个正整数Q,每行表示操作的个数,

 

如果第一个数是1,后接3个正整数,

 

表示在区间[a,b]内每个数增加X,如果是2,

 

表示操作2询问区间[a,b]的和是多少。

 

pascal选手请不要使用readln读入

输出描述 Output Description

对于每个询问输出一行一个答案

样例输入 Sample Input

3

1

2

3

2

1 2 3 2

2 2 3

样例输出 Sample Output

9

数据范围及提示 Data Size & Hint

数据范围

1<=n<=200000

1<=q<=200000

 
分块大法 此题用分块优于线段树
#include <ctype.h>
#include <cstdio>
#include <cmath>
#define N 500
typedef long long LL;
void read(LL &x)
{
    x=0;bool f=0;char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    x=f?(~x)+1:x;
}
LL C,m,belong[N*N],cnt,tag[N],sum[N],beg[N],en[N],n,a[N*N]; 
LL min(LL a,LL b) {return a>b?b:a;}
LL max(LL a,LL b) {return a>b?a:b;} 
void update(LL x,LL y,LL z)
{
    for(LL i=belong[x];i<=belong[y];i++)
    {
        if(x<=beg[i]&&y>=en[i]) tag[i]+=z,sum[i]+=(en[i]-beg[i]+1)*z;
        else for(LL j=max(beg[i],x);j<=min(en[i],y);j++) a[j]+=z,sum[i]+=z;
    }
}
LL query(LL x,LL y)
{
    LL ans=0;
    for(LL i=belong[x];i<=belong[y];i++)
    {
        if(x<=beg[i]&&y>=en[i]) ans+=sum[i];
        else for(LL j=max(beg[i],x);j<=min(en[i],y);j++) ans+=a[j]+tag[i];
    }
    return ans;
}
int main()
{
    read(n);
    for(LL i=1;i<=n;i++) read(a[i]);
    C=sqrt(n);
    for(LL i=1;i<=n;i+=C)
    {
        beg[++cnt]=i;
        en[cnt]=min(i+C-1,n);
    }
    for(LL i=1;i<=cnt;i++)
    {
        for(LL j=beg[i];j<=en[i];j++) belong[j]=i,sum[i]+=a[j];
    }
    read(m);
    for(LL type,x,y,z;m--;)
    {
        read(type);
        read(x);
        read(y);
        if(type==1)
        {
            read(z);
            update(x,y,z);
        }
        else printf("%lld\n",query(x,y));
    }
    return 0;
}

 

posted @ 2017-05-06 09:57  杀猪状元  阅读(178)  评论(0编辑  收藏  举报