描述
现在有一个有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;
}