描述
現在有一個有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;
}