您的位置:首页 > 其它

BOJ 451 田田的算术题

2014-08-02 20:54 232 查看
题意:给出一个数列。有两个操作:1.将给定范围内的项按照顺序加上一个等差数列的项。2.求出给定范围的数列项的和。

思路1:区间更改和区间求和的操作,很容易想到了线段树。由于等差数列满足可加性(即对相同范围内的数进行两次操作1,可以看做一次操作1的和),所以必定可以用线段树。

代码如下:

#include <bits/stdc++.h>

using namespace std;

template<class T>
inline bool read(T &n){
T x = 0, tmp = 1; char c = getchar();
while ((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
if (c == EOF) return false;
if (c == '-') c = getchar(), tmp = -1;
while (c >= '0' && c <= '9') x *= 10, x += (c - '0'), c = getchar();
n = x*tmp;
return true;
}

template <class T>
inline void write(T n) {
if (n < 0) {
putchar('-');
n = -n;
}
int len = 0, data[20];
while (n) {
data[len++] = n % 10;
n /= 10;
}
if (!len) data[len++] = 0;
while (len--) putchar(data[len] + 48);
}

typedef long long LL;

const int MAXN = 101000;

long long a[MAXN];
int T, M, N, Q, l, r, x, d;

struct interval{
int left, right, mid;
long long sum, delta, x;
bool lazy;
};

struct SegmentTree{
static const int MAX = 8 * 101000;
interval node[MAX];

inline int lson(int n){ return n << 1; }
inline int rson(int n){ return (n << 1) | 1; }

void build(int l, int r, int n){
int m = (l + r) / 2;
node
.left = l;
node
.right = r;
node
.mid = m;
node
.sum = node
.delta = node
.x = 0LL;
node
.lazy = false;

if (l == r){
node
.sum = a[l];
return;
}
build(l, m, lson(n));
build(m + 1, r, rson(n));
node
.sum += node[lson(n)].sum + node[rson(n)].sum;
}

void pushdown(int n){
if (node
.left == node
.right){
node
.lazy = false;
return;
}

//printf("[%d %d]",node
.left,node
.right);

int l = lson(n);
node[l].lazy = true;
node[l].delta += node
.delta;
node[l].x += node
.x;
long long n1 = node
.mid - node
.left + 1;
node[l].sum += node
.x * n1 + n1 * (n1 - 1) / 2 * node
.delta;

//printf("lson: %lld ",node[l].sum);

int r = rson(n);
node[r].lazy = true;
node[r].delta += node
.delta;
long long a1 = node
.x + (node
.mid - node
.left + 1) * node
.delta;
node[r].x += a1;
long long n2 = node
.right - node
.mid;
node[r].sum += a1 * n2 + n2 * (n2 - 1) / 2 * node
.delta;

//printf("rson: %lld\n",node[r].sum);

node
.lazy = false;
node
.delta = node
.x = 0LL;
}
long long sum(int l, int r, int n){
if(node
.left == node
.right)
return node
.sum;
if(l <= node
.left && node
.right <= r)
return node
.sum;

if(node
.lazy)
pushdown(n);

long long ans = 0LL;

if (l <= node
.mid)
ans += sum(l, r, lson(n));
if (r > node
.mid)
ans += sum(l, r, rson(n));

return ans;
}
void modify(int l, int r, long long x, long long d, int n){
if(node
.left == node
.right){
node
.sum += x;
return;
}

if (l <= node
.left && node
.right <= r){
node
.lazy = true;
node
.delta += d;
node
.x += x;
long long n1 = r - l + 1;
node
.sum += n1 * x + n1 *(n1 - 1) / 2 * d;

//printf("m: %d %d\n",l,r);
return;
}
if(node
.lazy)
pushdown(n);

long long nn = r - l + 1;
node
.sum += nn * x + nn * (nn - 1) / 2 * d;

if (r <= node
.mid)
modify(l, r, x, d, lson(n));
else if (l > node
.mid)
modify(l, r, x, d, rson(n));
else{
modify(l, node
.mid, x, d, lson(n));
modify(node
.mid + 1, r, x + (node
.mid - l + 1) * d, d, rson(n));
}
}
};

SegmentTree s;

int main(void)
{
//freopen("input.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
read(T);
while (T--){
read(N), read(M);
for (int i = 1; i <= N; ++i)
read(a[i]);
s.build(1, N, 1);
for (int i = 0; i < M; ++i){
read(Q), read(l), read(r);
if (Q == 2)
write(s.sum(l, r, 1)),puts("");
else{
read(x), read(d);
s.modify(l, r, x, d, 1);
}
}
}
return 0;
}


思路二:由于时限是10s,可以利用复杂度为n√n的块状数组。

每个块状数组维护整体的和,和数组内所有元素对等差数列的加减(同样具有可加性)。

如果查询或者修改区间有部分无法完全包含在块状数组中,则暴力的求和和更改。

注意对数组大小的选择。过大过小都不合适。

代码如下:

#include <bits/stdc++.h>

using namespace std;

template<class T>
inline bool read(T &n){
T x = 0, tmp = 1; char c = getchar();
while ((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
if (c == EOF) return false;
if (c == '-') c = getchar(), tmp = -1;
while (c >= '0' && c <= '9') x *= 10, x += (c - '0'), c = getchar();
n = x*tmp;
return true;
}

template <class T>
inline void write(T n) {
if (n < 0) {
putchar('-');
n = -n;
}
int len = 0, data[20];
while (n) {
data[len++] = n % 10;
n /= 10;
}
if (!len) data[len++] = 0;
while (len--) putchar(data[len] + 48);
}

const int B = 250;
const int MAX = 100100;
int T,M,N,Q;
int l,r,x,d;

long long a[MAX];

struct buc{
long long sum;
long long d,x;
void clear(){
sum = d = x = 0LL;
}
} bucket[MAX / B];

long long getsum(int l, int r){
long long ans = 0;
while(l < r && l % B != 0){
int id = l / B;
int pos = l - id * B;
ans += a[l] + bucket[id].x + pos * bucket[id].d;
l++;
}
while(l < r && r % B != 0){
r--;
int id = r / B;
int pos = r - id * B;
ans += a[r] + bucket[id].x + pos * bucket[id].d;
}
while(l < r){
int id = l / B;
ans += bucket[id].sum;
l += B;
}
return ans;
}

void update(int l,int r,long long x,long long d)
{
int cnt = 0;
while(l < r && l % B != 0){
int id = l / B;
long long aa = x + cnt * d;
a[l] += aa;
bucket[id].sum += aa;
cnt++,l++;
}
while(l + B < r){
int id = l / B;
bucket[id].sum += B * (x + cnt * d) + B *(B - 1) / 2 * d ;
bucket[id].x += x + cnt * d;
bucket[id].d += d;
l += B,cnt += B;
}
while(l < r){
int id = l / B;
long long aa = x + cnt * d;
a[l] += aa;
bucket[id].sum += aa;
cnt++,l++;
}
}

int main(void)
{
//freopen("input.txt","r",stdin);
//freopen("out.txt","w",stdout);
read(T);
while(T--){
read(N),read(M);
for(int i = 0 ; i < N / B + 1; i++)
bucket[i].clear();
for(int i = 0 ; i< N; ++i)
read(a[i]);
for(int i = 0 ; i< N; ++i){
int id = i / B;
bucket[id].sum += a[i];
}
for(int i = 0; i< M; ++i){
read(Q);
if(Q == 2){
read(l),read(r);
printf("%lld\n",getsum(l-1,r));
}
else{
read(l),read(r),read(x),read(d);
update(l-1,r,(long long)x,(long long)d);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: