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 <string.h>
#include <stdio.h>
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;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章