鏈上二次求和
阿新 • • 發佈:2018-04-28
freopen algo ++ div chan class code names ret
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <algorithm> using namespace std; const int MAXN = 202020; const int MOD = 1000000007; const int REV2 = (MOD + 1) / 2; const int REV6 = (MOD + 1) / 6; inline int get_polynomial_sum(int a0, int a1, inta2, int x) { return (((long long)a2 * (2 * x + 1) % MOD * REV6 + (long long)a1 * REV2) % MOD * (x + 1) + a0) % MOD * x % MOD; } int n, m, a[MAXN]; class Segment_tree { public: int st, ed; Segment_tree *l, *r; int sum; int a[3];//lazy tags //a[0]: add 1, 1, 1, 1, ... //a[1]: add 1, 2, 3, 4, ...//a[2]: add 1, 4, 9, 16, ... Segment_tree(int st, int ed): st(st), ed(ed), l(0), r(0), sum(0) { a[0] = a[1] = a[2] = 0; } Segment_tree(int st, int ed, Segment_tree *l, Segment_tree *r): st(st), ed(ed), l(l), r(r) { a[0] = a[1] = a[2] = 0; update(); } int len() const{ return ed - st + 1; } void add(int b0, int b1, int b2) { a[0] = (a[0] + b0) % MOD; a[1] = (a[1] + b1) % MOD; a[2] = (a[2] + b2) % MOD; sum = (sum + get_polynomial_sum(b0, b1, b2, len())) % MOD; } void down() { if(!a[0] && !a[1] && !a[2]) return; l->add(a[0], a[1], a[2]); int llen = l->len(); r->add((((long long)a[2] * llen + a[1]) % MOD * llen + a[0]) % MOD, ((long long)a[2] * llen * 2 + a[1]) % MOD, a[2]); a[0] = a[1] = a[2] = 0; } void update() { sum = (l->sum + r->sum) % MOD; } }; Segment_tree *root; Segment_tree *build(int st, int ed) { return st == ed ? new Segment_tree(st, ed) : new Segment_tree(st, ed, build(st, (st + ed) >> 1), build(((st + ed) >> 1) + 1, ed)); } long long query(Segment_tree *p, int st, int ed) { if(p->st >= st && p->ed <= ed) return p->sum; p->down(); if(ed <= p->l->ed) return query(p->l, st, ed); if(st >= p->r->st) return query(p->r, st, ed); return query(p->l, st, ed) + query(p->r, st, ed); } void add(Segment_tree *p, int st, int ed, int b0, int b1, int b2) { // if(p == root) // printf("add: [%d, %d] %d + %dx + %dx^2\n", st, ed, b0, b1, b2); if(p->st >= st && p->ed <= ed) { p->add(b0, b1, b2); return; } p->down(); if(ed <= p->l->ed) add(p->l, st, ed, b0, b1, b2); else if(st >= p->r->st) add(p->r, st, ed, b0, b1, b2); else { add(p->l, st, p->l->ed, b0, b1, b2); int llen = p->l->ed - st + 1; add(p->r, p->r->st, ed, (((long long)b2 * llen + b1) % MOD * llen + b0) % MOD, ((long long)b2 * llen * 2 + b1) % MOD, b2); } p->update(); } void change(int u, int v, int d) { if(u > v) swap(u, v); int len = v - u + 1; v = n - v + 1; add(root, 1, n, 0, (long long)len * d % MOD, 0); if(u + 1 <= n) add(root, u + 1, n, 0, (long long)(MOD - d) * REV2 % MOD, (long long)(MOD - d) * REV2 % MOD); if(u + len + 1 <= n) add(root, u + len + 1, n, 0, (long long)d * REV2 % MOD, (long long)d * REV2 % MOD); if(v + 1 <= n) add(root, v + 1, n, 0, (long long)(MOD - d) * REV2 % MOD, (long long)(MOD - d) * REV2 % MOD); if(v + len + 1 <= n) add(root, v + len + 1, n, 0, (long long)d * REV2 % MOD, (long long)d * REV2 % MOD); } int main() { // freopen("sum.in", "r", stdin); // freopen("sum.out", "w", stdout); scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); root = build(1, n); for(int i = 1; i <= n; i++) if(a[i]) change(i, i, a[i]); for(int i = 1; i <= m; i++) { int tp, u, v, d; scanf("%d%d%d", &tp, &u, &v); if(tp == 1) { scanf("%d", &d); change(u, v, d); } else { printf("%d\n", (int)(query(root, u, v) % MOD)); } } return 0; }
鏈上二次求和