题目的意思很明确,有两种操作,一种是计算一个数列的第 a 到 第b的和,另一种是第 a 到 第 b 之间的数加上 c。由于这些操作的数目很大,用普通的办法无法办到,会超时。
对于这类问题,用线段树可以很好解决。对于线段树还只是学习阶段,还不是很熟,需要多加练习与理解。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef __int64 ll; const int DAT_Size = (1 << 18) - 1; const int MAX_N = 100000 + 1; const int MAX_Q = 100000 + 1; //存储输入数据 int N, Q; int A[MAX_N]; char T[MAX_Q]; int L[MAX_Q], R[MAX_Q], X[MAX_Q]; //线段树 ll data[DAT_Size], data1[DAT_Size]; //取最大值 int max(int x, int y) { return x > y ? x : y; } //取最小值 int min(int x, int y) { return x < y ? x : y; } //对于区间[a,b)加上x //k是结点的编号,对应的区间为[l, r) void add(int a, int b, int x, int k, int l, int r) { if(a <= l && r <= b) //找到区间,加上x data[k] += x; else if(l < b && a < r) { data1[k] += (min(b, r) - max(a, l)) * x; add(a, b, x, 2 * k + 1, l, (l + r) / 2); add(a, b, x, 2 * k + 2, (l + r) / 2, r); } } //计算[a, b)的和 ll sum(int a, int b, int k, int l, int r) { //<span style="font-family: Arial, Helvetica, sans-serif;">区间[a,b)不包含[l, r)</span> if(b <= l || r <= a) return 0; else if(a <= l && b >= r)//区间[a,b)完全包含[l,r) { return data[k] * (r - l) + data1[k]; } else { __int64 ans = (min(b, r) - max(a, l)) * data[k]; ans += sum(a, b, k * 2 + 1, l, (l + r) / 2); ans += sum(a, b, k * 2 + 2, (l + r) / 2, r); return ans; } } void solve() { for(int i = 0; i < N; i++) //将线段树的区间定在[1, N) add(i + 1, i + 2, A[i], 0, 1, N + 1); for(int j = 0; j < Q; j++) { if(T[j] == 'C') add(L[j], R[j] + 1, X[j], 0, 1, N + 1); else printf("%I64d\n", sum(L[j], R[j] + 1, 0, 1, N + 1)); } } int main() { memset(data, 0, sizeof(data)); memset(data1, 0, sizeof(data1)); scanf("%d%d", &N, &Q); for(int i = 0; i < N; i++) //输入数据 scanf("%d", &A[i]); getchar(); for(int j = 0; j < Q; j++) { scanf("%c", &T[j]); if(T[j] == 'Q') scanf("%d%d", &L[j], &R[j]); else scanf("%d%d%d", &L[j], &R[j], &X[j]); getchar(); } solve(); return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
北大ACM3468——A Simple Problem with Integers~~线段树的应用
原文地址:http://blog.csdn.net/qq_25425023/article/details/46955245