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
分析 Analysis
分块!
分块除了细节难调外没有什么问题,,,只要理解到位并且有自己的码风即可。
更具体的分析:
[Codevs] 1081 线段树练习 2 ----“分块!”
代码 Code
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #define maxn 1000000 5 using namespace std; 6 7 long long block[maxn],size,sum[maxn],add[maxn],arr[maxn],n,q,swi; 8 9 void modify(){ 10 long long a,b,c; 11 scanf("%lld%lld%lld",&a,&b,&c); 12 a--,b--; 13 14 for(long long i = a;i <= min(block[a]*size-1,b);i++) 15 arr[i] += c,sum[block[i]] += c; 16 if(block[a] != block[b]){ 17 for(long long i = (block[b]-1)*size;i <= b;i++) 18 arr[i] += c; 19 sum[block[b]] += (b-(block[b]-1)*size+1)*c; 20 }for(long long i = block[a]+1;i < block[b];i++) 21 sum[i] += size*c,add[i] += c; 22 } 23 24 void query(){ 25 long long a,b; 26 scanf("%lld%lld",&a,&b); 27 a--,b--; 28 long long ans = 0; 29 30 for(long long i = a;i <= min(block[a]*size-1,b);i++) 31 ans += arr[i]+add[block[i]]; 32 if(block[a] != block[b]){ 33 for(long long i = (block[b]-1)*size;i <= b;i++) 34 ans += arr[i]+add[block[i]]; 35 }for(long long i = block[a]+1;i < block[b];i++) 36 ans += sum[i]; 37 38 printf("%lld\n",ans); 39 } 40 41 int main(){ 42 scanf("%lld",&n); 43 size = (long long)sqrt(n)+1; 44 for(long long i = 0;i < n;i++) block[i] = i/size+1; 45 for(long long i = 0;i < n;i++) scanf("%lld",&arr[i]); 46 for(long long i = 0;i < n;i++) sum[block[i]] += arr[i]; 47 48 scanf("%lld",&q); 49 for(long long i = 0;i < q;i++){ 50 scanf("%lld",&swi); 51 if(swi%2) modify(); 52 else query(); 53 } 54 55 return 0; 56 }