南邮OJ 2027 操作序列

链接:http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=2027

题目:

操作序列

时间限制(普通/Java):1000MS/3000MS          运行内存限制:65536KByte
总提交:146            测试通过:26

描述

给出一初始序列a1, a2,...,an,下面有m个操作(x, l, r) : 对于a[l], a[l+1],...,a[r]都加上x.
输出m个操作结束后的序列.

输入

第一行两个整数n,m(0 <= n,m <= 100000),n表序列{A}的长度, m表操作的个数。
第二行有n 个整数ai(-10000 <= ai <= 10000)。
下面m行,每一行表示一个操作,一个操作表示为3个整数x, l, r(1 <= l <= r <= n, |x|<=1000)。

输出

输出结果序列。数据已改正,行末没有空格!

样例输入

5 3
1 2 3 -4 5 
2 1 1
-3 3 5
0 1 5

样例输出

3 2 0 -7 2

提示

null


解题思路:

解法1:

这道题目,主要涉及的就是一个区间更新的问题。将数组a的[L,R]区间内的每个元素加x。我们可以这样来解决这个问题,将区间[L,R]分解为[1,R]和[1,L-1],这样原问题就可以转化为,将[1,R]的每个元素加x,再将[1,L-1]的每个元素加(-x),我们可以用一个数组sum来记录这些更新。sum[i] = x,表示对数组a从第1个元素到第i个元素,每个元素加上x。这样的话最后我们对a的更新就变成了这样:a[i] = a[i] + sum[i] + sum[i+1] + sum[i+2] + ... + sum[n];我们可以优化一下,将sum数组进行处理,使其成为前缀和数组(sum[i] = b[1] + b[2] + ... + b[i] )。这样对a的更新就变成了:a[i] = a[i] + sum[n] - sum[i-1];

解法2:

线段树区间更新。


解法1代码:

#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 100010;
int a[MAXN], sum[MAXN];//sum[i] = x数组,表示a[i]数组从1到i每个元素加x 

int main()
{
	std::ios::sync_with_stdio(false);
	
	memset(a, 0, sizeof(a));
	memset(sum, 0, sizeof(sum));
	int n, m;	cin >> n >> m;
	
	for(int i = 1; i <= n; i++)
		cin >> a[i];
		
	while(m--)
	{
		int l, r, x;
		cin >> x >> l >> r;
		sum[r]	+= x;
		sum[l-1]-= x;
	}
	
	for(int i = 1; i <= n; i++)
		sum[i] += sum[i-1];
	
	for(int i = 1; i <= n; i++)
		a[i] += (sum[n] - sum[i-1]);
	
	for(int i = 1; i <= n; i++)
	{
		if(i - 1) cout << " ";
		cout << a[i];
	}
	cout << endl;
		
	return 0;
}


解法2代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int MAXN = 100010;
struct Tree
{
	int left, right, x;
};
Tree tree[4 * MAXN];
int first = 1;

void build_tree(int l, int r, int i)
{
	tree[i].left  = l;
	tree[i].right = r;
	tree[i].x     = 0;
	if(l == r)
	{
		return ;
	}
	int mid = (l + r) >> 1;
	build_tree(l, mid, i + i);
	build_tree(mid + 1, r, i + i + 1);
}

void update(int l, int r, int i, int x)
{
	if(l == tree[i].left && r == tree[i].right)
	{
		tree[i].x += x;
		return;
	}

	int mid = (tree[i].left + tree[i].right) >> 1;
	if(r <= mid)
	{
		update(l, r, i + i, x);
	}
	else
	{
		if(l > mid)
		{
			update(l, r, i + i + 1, x);
		}
		else
		{
			update(l, mid, i + i, x);
			update(mid + 1, r, i + i + 1, x);
		}
	}
	
}

void print(int l, int r, int i, int num)
{
	if(l == r)
	{
		if(!first) cout << " ";
		else first = 0;
		cout << tree[i].x + num;
		return ;
	}
	int mid = (l + r) >> 1;
	print(l, mid, i + i, tree[i].x + num);
	print(mid + 1, r, i + i + 1, tree[i].x + num);
}

int main()
{
	std::ios::sync_with_stdio(false);
	int n, m;	cin >> n >> m;
	
	tree[1].left = 1, tree[1].right = n, tree[1].x = 0;
	
	build_tree(1, n, 1);
	
	for(int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		update(i, i, 1, x);
	}
	
	while(m--)
	{
		int x, l, r;
		cin >> x >> l >> r;
		update(l, r, 1, x);
	}
	
	print(1, n, 1, 0);
	
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值