JZOJ #4724 斐波那契 (平衡规划实现根号算法)
题目描述:
$F$表示斐波那契数列,给定原始数列,每次操作对$[l,r]$内的数加上$F[i-l+1](l\leq{i}\leq{r})$,或询问区间$[l,r]$的和。
解题思路:
我们发现对于修改操作可以$O(1)$实现。只要用一新数组$D$记录,对于$[l,r]$的修改,将$D[l]+=1,D[r+1]-=F[r-l+2],D[r+2]-=F[r-l+1]$,然后询问的时候令$D[i]+=D[i-1]+D[i-2]$。$D$就是总的修改和。然而询问要$O(N)$。我们怎样才能平衡修改和询问呢。这要用到平衡规划的思想, 每修改$k$次才重构一次$D$数组。对于一次询问,答案就是上一次重构的$D$数组和未将$D$操作的修改的总贡献。$k$取$\sqrt{N}$的话,复杂度是$O(\sqrt{N}M)$。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 using namespace std; 6 7 const int N = 1e5 + 10, mo = 1e9 + 9; 8 int n, m, a[N], f[N], suf[N], sua[N], que[N][2], tot, siz, d[N]; 9 10 void init() { 11 f[1] = f[2] = 1; 12 for (int i = 3; i <= n; i ++) f[i] = (f[i - 1] + f[i - 2]) % mo; 13 for (int i = 1; i <= n; i ++) suf[i] = (suf[i - 1] + f[i]) % mo; 14 } 15 16 void rebuild() { 17 memset(d, 0, sizeof d); 18 for (int i = 1; i <= tot; i ++) { 19 int l = que[i][0], r = que[i][1]; 20 (d[l] += 1) %= mo; 21 (d[r + 1] += mo - f[r - l + 2]) %= mo; 22 (d[r + 2] += mo - f[r - l + 1]) %= mo; 23 } 24 for (int i = 1; i <= n; i ++) (d[i] += (d[i - 1] + d[i - 2]) % mo) %= mo, (a[i] += d[i]) %= mo, sua[i] = (sua[i - 1] + a[i]) % mo; 25 tot = 0; 26 } 27 28 void query(int l, int r) { 29 int ans = (sua[r] - sua[l - 1] + mo) % mo; 30 for (int i = 1; i <= tot; i ++) { 31 int x = que[i][0], y = que[i][1]; 32 if (r < x || l > y) continue; 33 (ans += (suf[min(y, r) - x + 1] - suf[max(x, l) - x] + mo) % mo) %= mo; 34 } 35 printf("%d\n", ans); 36 } 37 38 int main() { 39 scanf("%d %d", &n, &m); 40 init(); 41 siz = sqrt(m); 42 for (int i = 1; i <= n; i ++) scanf("%d", &a[i]), sua[i] = (sua[i - 1] + a[i]) % mo; 43 while (m --) { 44 int p, l, r; 45 scanf("%d %d %d", &p, &l, &r); 46 if (p == 1) { 47 que[++ tot][0] = l, que[tot][1] = r; 48 if (tot == siz) rebuild(); 49 } else query(l, r); 50 } 51 return 0; 52 }