第一次看到段更斐波那契数列的,整个人都不会好了。事后看了题解才明白了一些。
首先利用二次剩余的知识,以及一些数列递推式子有下面的
至于怎么解出x^2==5(mod 10^9+9),我就不知道了,但是要用的时候可以枚举一下,把这些参数求出来之后就题目就可以转化为维护等比数列。
由于前面的常数可以最后乘,所以就等于维护两个等比数列好了。
下面我们来看如何维护一个等比数列。假如我对区间[L,R]的加上1,2,4,8...2^n的话,那么我只需要加一个标记x表示这个区间被加了多少次这样的2^n.
举个例子 [1,8] 上加一个等比数列,我只需要x+=1,就可以了,当我的区间往下传的时候,[1,4]这个区间的x+=1,[5,8]这个区间x+=2^4*1
这个利用的就是公比相同的数列相加仍然是等比数列的性质。求和利用的则是 a1(q^n-1)/(q-1),所以只需要预处理出q-1的逆元还有q^n我们就可以根据区间信息很快的求出和了。
在本题中 q-1的逆=q,首项也是q,所以前n项和就是 qi^(n+2)-qi^2(i=1,2). 其实主要就是考虑怎么维护等比数列的问题。
然后我看到在别人的AC的方法里还有这么一种神方法,他预先设定了一个阈值K,当当前的更新操作数j<K的时候,它就用一个类似于树状数组段更的方法,用一个 d数组去存内容,譬如它要在区间 [3,6]上加一段fibonacci
原来:
id 0 1 2 3 4 5 6 7 8 9 10
d 0 0 0 0 0 0 0 0 0 0 0
更新:
id 0 1 2 3 4 5 6 7 8 9 10
d 0 0 0 1 0 0 0 -5 -3 0 0
我们可以发现,当利用 d[i]=d[i]+d[i-1]+d[i-2] i由小到大更新后就会得到
id 0 1 2 3 4 5 6 7 8 9 10
d 0 0 0 1 1 2 3 0 0 0 0
所以对于[L,R]上加一段操作,我们可以d[L]+=1; d[R+1]-=f[R-L+2],d[R+2]=f[R-L+1];
所以假如我更新了m次,我每次更新的复杂度是O(1),我把所有数求出来一次的复杂度是O(n)
然后他的算法是这样的,j<K的时候更新的时候按照上述方法更新,询问的话则是将询问区间和更新的区间求交,把交的和加上去。
当j==K的时候,利用d还原出新的a,把当前的区间清0.
不难发现,复杂度应该为O(mn/K+mK),当mn/K=mK的时候取最小值,所以复杂度是O(mn^(1/2)).
然后由于CF可以承受10 ^8的运算,所以打个擦边球AC了。下面贴两份代码:
#pragma warning(disable:4996) #include <iostream> #include <cstdio> #include <cstring> #include <string> #include <vector> #include <cmath> #include <algorithm> using namespace std; #define ll long long #define mod 1000000009 #define maxn 300500 ll bas = 276601605; ll q1 = 691504013; ll q2 = 308495997; ll xx5 = 383008016; ll inv5 = 200000002; ll inv2 = 500000005; ll invq1; ll invq2; ll pow_mod(ll a, ll n){ ll ret = 1; while (n){ if (n & 1) ret = ret*a%mod; n >>= 1; a = a*a%mod; } return ret; } ll a[maxn], b[maxn]; int n, m; ll val[maxn]; ll sum[maxn]; struct Node { int l, r; ll ax, bx; ll sum; }N[maxn << 2]; void build(int i, int L, int R){ N[i].l = L; N[i].r = R; N[i].ax = N[i].bx = N[i].sum = 0; if (L == R){ return; } int M = (L + R) >> 1; build(i << 1, L, M); build(i << 1 | 1, M + 1, R); } void pushDown(int i){ ll av = N[i].ax, bv = N[i].bx; if (N[i].ax != 0 || N[i].bx != 0){ N[i << 1].ax = (N[i << 1].ax + av) % mod; N[i << 1].bx = (N[i << 1].bx + bv) % mod; int len = N[i << 1].r - N[i << 1].l + 1; N[i << 1 | 1].ax = (N[i << 1 | 1].ax + av*a[len] % mod) % mod; N[i << 1 | 1].bx = (N[i << 1 | 1].bx + bv*b[len] % mod) % mod; int len2 = N[i << 1 | 1].r - N[i << 1 | 1].l + 1; N[i << 1].sum = (N[i << 1].sum + av*(((a[len + 2] - a[2]) % mod + mod) % mod) % mod) % mod; N[i << 1].sum = (N[i << 1].sum - bv*(((b[len + 2] - b[2]) % mod + mod) % mod) % mod) % mod; N[i << 1 | 1].sum = (N[i << 1 | 1].sum + av*a[len] % mod*(a[len2 + 2] - a[2] + mod) % mod) % mod; N[i << 1 | 1].sum = ((N[i << 1 | 1].sum - bv*b[len] % mod*(b[len2 + 2] - b[2] + mod) % mod) % mod + mod) % mod; N[i].ax = N[i].bx = 0; } } void pushUp(int i){ N[i].sum = (N[i << 1].sum + N[i << 1 | 1].sum) % mod; } void update(int i, int L, int R, ll x, ll y){ if (N[i].l == L&&N[i].r == R){ N[i].ax = (N[i].ax + x) % mod; N[i].bx = (N[i].bx + y) % mod; int len = R - L + 1; N[i].sum = (N[i].sum + x*(a[len + 2] - a[2] + mod) % mod + mod) % mod; N[i].sum = (N[i].sum - y*(b[len + 2] - b[2] + mod) % mod + mod) % mod; return; } pushDown(i); int M = (N[i].l + N[i].r) >> 1; if (R <= M){ update(i << 1, L, R, x, y); } else if (L > M){ update(i << 1 | 1, L, R, x, y); } else{ int len = (M - L + 1); update(i << 1, L, M, x, y); update(i << 1 | 1, M + 1, R, a[len] * x%mod, b[len] * y%mod); } pushUp(i); } ll query(int i, int L, int R){ if (N[i].l == L&&N[i].r == R){ return N[i].sum; } pushDown(i); int M = (N[i].l + N[i].r) >> 1; if (R <= M){ return query(i << 1, L, R); } else if (L > M){ return query(i << 1 | 1, L, R); } else{ return (query(i << 1, L, M) + query(i << 1 | 1, M + 1, R)) % mod; } pushUp(i); } int main() { scanf("%d%d", &n, &m); a[0] = b[0] = 1; for (int i = 1; i <= n + 5; i++){ a[i] = a[i - 1] * q1%mod; b[i] = b[i - 1] * q2%mod; } val[0] = 0; sum[0] = 0; for (int i = 1; i <= n; i++){ scanf("%I64d", val + i); sum[i] = (sum[i - 1] + val[i]) % mod; } build(1, 1, n); int oper, l, r; for (int i = 0; i < m; i++){ scanf("%d%d%d", &oper, &l, &r); if (oper == 1) update(1, l, r, 1, 1); else { ll res = query(1, l, r); res = (res + mod) % mod; res = res*bas%mod; res = (res + ((sum[r] - sum[l - 1]) % mod+mod)%mod) % mod; printf("%I64d\n", res); } } return 0; }
#pragma warning(disable:4996) #include <iostream> #include <cstdio> #include <cstring> #include <string> #include <vector> #include <cmath> #include <map> #include <algorithm> #include <queue> using namespace std; #define ll long long #define maxn 310000 #define K 800 #define mod 1000000009 ll a[maxn]; ll s[maxn]; ll f[maxn], g[maxn]; int n, m; int kk; ll d[maxn]; int l[K + 50], r[K + 50]; int main() { while (cin >> n >> m) { f[0] = 0; f[1] = 1; g[0] = 0; g[1] = 1; for (int i = 2; i <= n+10; i++){ f[i] = (f[i - 1] + f[i - 2]) % mod; g[i] = (g[i - 1] + f[i]) % mod; } s[0] = 0; for (int i = 1; i <= n; i++){ scanf("%I64d", &a[i]); s[i] = (s[i - 1] + a[i]) % mod; } memset(d, 0, sizeof(d)); int oper, li, ri; kk = 0; for (int i = 0; i < m; i++){ scanf("%d%d%d", &oper,&li,&ri); if (oper == 1){ l[kk] = li, r[kk] = ri; d[li] = (d[li] + 1) % mod; d[ri + 1] = ((d[ri + 1] - f[ri - li + 2]) % mod + mod) % mod; d[ri + 2] = ((d[ri + 2] - f[ri - li + 1]) % mod + mod) % mod; kk++; if (kk == K){ for (int i = 1; i <= n; i++){ if (i == 1) a[i] = (a[i] + d[i]) % mod; else { d[i] = (d[i] + d[i - 1] + d[i - 2]) % mod; a[i] = (a[i] + d[i]) % mod; } s[i] = (s[i - 1] + a[i]) % mod; } memset(d, 0, sizeof(d)); kk = 0; } } else{ ll res = ((s[ri] - s[li - 1]) % mod + mod) % mod; for (int j = 0; j < kk; j++){ int lb = max(l[j], li); int rb = min(r[j], ri); if (lb <= rb){ res = (res + (g[rb - l[j] + 1] - g[lb - l[j]]) % mod + mod) % mod; } } printf("%I64d\n", res); } } } return 0; }
Codeforces446C DZY Loves Fibonacci Numbers(线段树 or 分块?)