hihoCoder #1116 : 计算

描述

现在有一个有n个元素的数组a1, a2, ..., an

记f(i, j) = ai * ai+1 * ... * aj。

初始时,a1 = a2 = ... = an = 0,每次我会修改一个ai的值,你需要实时反馈给我 ∑1 <= i <= j <= n f(i, j)的值 mod 10007。

输入

第一行包含两个数n(1<=n<=100000)和q(1<=q<=500000)。

接下来q行,每行包含两个数i, x,代表我把ai的值改为了x。

输出

分别输出对应的答案,一个答案占一行。

样例输入
5 5
1 1
2 1
3 1
4 1
5 1
样例输出
1
3
6
10
15

题意:求出在每次修改后的 满足以上描述的公式的求和结果。

思路:一眼看去这么大的数,肯定是线段树。怎么做呢?每一个线段树节点,记录四个信息,

1.该区间从左端点开始的所有连续序列的乘积和     数组l记录

2.该区间从右端点开始的所有连续序列的乘积和     数组r记录

3.当前区间所有元素的乘积                                     数组all记录

4.当前区间的答案                                                  数组seg记录

这样两个区间合并的时候,采用如下方式合并

all[root] = all[Lroot] * all[Rroot]      很容易理解,两个区间的全部元素乘起来就是合并后区间的全部元素乘积

l[root] = l[Rroot]*all[Lroot] + l[Lroot]    左连续的结果除了左区间的左连续结果,还有左区间的全部元素跟右区间

左连续结果拼到一起的结果。

r[root] = r[Lroot]*all[Rroot] + r[Rroot]  同左区间的思路一样

seg[root] = seg[Lroot] + seg[Rroot] + r[Lroot] * l[Rroot]  左右区间的结果加

左区间右连续跟右区间左连续拼起来的结果,就是当前区间的结果

#include 
#include 
using namespace std;
const int N = 100100;
typedef long long ll;
int mod = 10007;
int seg[N<<2];
int all[N<<2];
int l[N<<2];
int r[N<<2];

void pushUp(int rt)
{
	seg[rt] = (seg[rt<<1] + seg[rt<<1|1] + r[rt<<1]*l[rt<<1|1]) % mod;
	l[rt] = (l[rt<<1] + l[rt<<1|1] * all[rt<<1]) % mod;
	r[rt] = (r[rt<<1|1] + r[rt<<1] * all[rt<<1|1]) % mod;
	all[rt] = all[rt<<1] * all[rt<<1|1] % mod;
}
void update(int l, int r, int rt, int id, int x)
{
	if (l == r)
	{
		seg[rt] = ::l[rt] = ::r[rt] = all[rt] = x;
		return;
	}
	int mid = (l + r)>>1;
	if (id <= mid)
		update(l, mid, rt<<1, id, x);
	else if (id > mid)
		update(mid + 1, r, rt<<1|1, id, x);
	pushUp(rt);
}
int main() 
{
  int n, q;
  while (~scanf("%d%d", &n, &q))
  {
  	memset(seg, 0, sizeof(seg));
  	memset(all, 0, sizeof(all));
  	memset(l, 0, sizeof(l));
  	memset(r, 0, sizeof(r));
  	while (q--)
  	{
  		int i, x;
  		scanf("%d%d", &i, &x);
  		update(1, n, 1, i, x);
  		printf("%d\n", seg[1]);
  	}
  }

  return 0;
}




你可能感兴趣的:(线段树,acm)