链上二次求和
题目背景:
分析:线段树
没想到BJOI也能有两道数据结构,跟SCOI一样······对于一个子区间[l, r],就是sum[r] - sum[l - 1],sum[i]表示前i个数的和,也就是前缀和数组,那么我们可以发现对于一个sum[i],在询问长度为[l, r]之间的区间和的和中,是可以固定sum[i]的计算次数的,为了方便起见,我们把[l, r]的询问拆成两段为询问[l, n]的答案减去[r + 1, n]的答案。对于一个询问[l, n],sum[i]作为左端点,会被使用(n - i - l + 1)次,从i + l开始到n,作为右端点会被使用(i - l + 1)次,0到i - l都是可以的,然后能够被作为左端点的是0 ~ n - l,能够作为右端点的是l ~ n,不能直接合并上面两个式子,因为不能作为端点的部分会出现负值。那么考虑对于[l, pos]的询问:
那么,我们只需要支持询问区间的sum[i]之和,以及区间的i * sum[i]之和,然后修改相当于[l, r]的区间加等差数列,和[r + 1, n]的区间加,大部分的实现都比较简单,唯一有一点就是如何在区间加等差数列的情况下,维护i * sum[i],考虑对于区间,l, l + 1, l + 2, l + 3·····r - 1, r加上x + d, x + 2 * d, x + 3 * d·····我们定义区间长度len = (r - l +1),首先对于x,我们应该加上x * (l + r) * len / 2,这个很显然,剩下的就是d * l + 2d * (l + 1) + 3d * (l + 2)····考虑d * l的系数应该是,(1 + 2 + … + len),所以再加上d * l * (len + 1) * len / 2,最后是对于d的系数,是(0 * 1 + 1 * 2 + 2 * 3 + ··· + (len - 1) * len)这个我们可以直接预处理出来,定义num[len] = (0 * 1 + 1 * 2 + ··· + (len - 1) * len),所以直接再加上num[len] * d就可以了。也就是说:
sum2[l, r] += x * (l + r) * len / 2;
sum2[l, r] += d * l * (len + 1) * len / 2;
sum2[l, r] += num[len] * d;
其他的就都非常好维护了。复杂度O(nlogn),常数大到上天,取膜太多。
(PS:出题人跟本没打算让你过,中间很多地方的u, v都是反的,并没有告诉你u <= v,直接玩就死定了)
Source:
/* created by scarlyw */ #include <cstdio> #include <string> #include <algorithm> #include <cstring> #include <iostream> #include <cmath> #include <cctype> #include <vector> #include <set> #include <queue> #include <ctime> #include <bitset> inline char read() { static const int IN_LEN = 1024 * 1024; static char buf[IN_LEN], *s, *t; if (s == t) { t = (s = buf) + fread(buf, 1, IN_LEN, stdin); if (s == t) return -1; } return *s++; } // /* template<class T> inline void R(T &x) { static char c; static bool iosig; for (c = read(), iosig = false; !isdigit(c); c = read()) { if (c == -1) return ; if (c == '-') iosig = true; } for (x = 0; isdigit(c); c = read()) x = ((x << 2) + x << 1) + (c ^ '0'); if (iosig) x = -x; } //*/ const int OUT_LEN = 1024 * 1024; char obuf[OUT_LEN]; char *oh = obuf; inline void write_char(char c) { if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf; *oh++ = c; } template<class T> inline void W(T x) { static int buf[30], cnt; if (x == 0) write_char('0'); else { if (x < 0) write_char('-'), x = -x; for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48; while (cnt) write_char(buf[cnt--]); } } inline void flush() { fwrite(obuf, 1, oh - obuf, stdout), oh = obuf; } /* template<class T> inline void R(T &x) { static char c; static bool iosig; for (c = getchar(), iosig = false; !isdigit(c); c = getchar()) if (c == '-') iosig = true; for (x = 0; isdigit(c); c = getchar()) x = ((x << 2) + x << 1) + (c ^ '0'); if (iosig) x = -x; } //*/ // #define int long long const int MAXN = 200000 + 10; const int mod = 1000000000 + 7; int n, m, type, l, r, x; long long a[MAXN], num[MAXN]; inline void add(long long &x, int t) { x += t, (x >= mod) ? (x -= mod) : x; } inline int fix(int a, int b) { return (a += b), (a >= mod) ? (a -= mod) : a; } struct node { long long fir, d, sum1, sum2; } tree[MAXN << 2 | 1]; struct data { long long sum1, sum2; data() {} data(long long sum1, long long sum2) : sum1(sum1), sum2(sum2) {}; inline data operator + (const data &a) const { return data(sum1 + a.sum1, sum2 + a.sum2); } } ; inline void update(int k) { tree[k].sum1 = fix(tree[k << 1].sum1, tree[k << 1 | 1].sum1); tree[k].sum2 = fix(tree[k << 1].sum2, tree[k << 1 | 1].sum2); } inline void modify(int k, int l, int r, int fir, int d) { long long len = r - l + 1, t = len * (len + 1) / 2 % mod; add(tree[k].fir, fir), add(tree[k].d, d); tree[k].sum1 = (tree[k].sum1 + len * fir + t * d) % mod; tree[k].sum2 = (tree[k].sum2 + len * (l + r) / 2 % mod * fir + t * d % mod * l + num[len] * d) % mod; } inline void push_down(int k, int l, int mid, int r) { if (tree[k].fir == 0 && tree[k].d == 0) return ; modify(k << 1, l, mid, tree[k].fir, tree[k].d); modify(k << 1 | 1, mid + 1, r, (tree[k].fir + tree[k].d * (mid + 1 - l)) % mod, tree[k].d); tree[k].fir = tree[k].d = 0; } inline void build(int k, int l, int r) { if (l == r) { tree[k].sum1 = a[l], tree[k].sum2 = (long long)l * a[l] % mod; return ; } int mid = l + r >> 1; build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r), update(k); } inline void modify(int k, int l, int r, int ql, int qr, int fir, int d) { if (ql == l && r == qr) return modify(k, l, r, fir, d); int mid = l + r >> 1; push_down(k, l, mid, r); if (qr <= mid) modify(k << 1, l, mid, ql, qr, fir, d); else if (ql > mid) modify(k << 1 | 1, mid + 1, r, ql, qr, fir, d); else modify(k << 1, l, mid, ql, mid, fir, d), modify(k << 1 | 1, mid + 1, r, mid + 1, qr, fix(fir, (long long)(mid + 1 - ql) * d % mod), d); update(k); } inline data query(int k, int l, int r, int ql, int qr) { if (ql <= l && r <= qr) return data(tree[k].sum1, tree[k].sum2); int mid = l + r >> 1; push_down(k, l, mid, r); if (qr <= mid) return query(k << 1, l, mid, ql, qr); else if (ql > mid) return query(k << 1 | 1, mid + 1, r, ql, qr); else return query(k << 1, l, mid, ql, qr) + query(k << 1 | 1, mid + 1, r, ql, qr); } inline void modify(int l, int r, int d) { if (l > r) std::swap(l, r); modify(1, 0, n, l, r, 0, d); if (r != n) modify(1, 0, n, r + 1, n, (long long)(r - l + 1) * d % mod, 0); } inline long long query(int pos) { data s1 = query(1, 0, n, 0, n - pos), s2 = query(1, 0, n, pos, n); s1.sum1 %= mod, s1.sum2 %= mod, s2.sum1 %= mod, s2.sum2 %= mod; long long ans = s2.sum2 - s2.sum1 * (pos - 1) + s1.sum2 - s1.sum1 * (n - pos + 1); return ans; } inline void query(int l, int r) { if (l > r) std::swap(l, r); W(((query(l) - ((r == n) ? 0 : query(r + 1))) % mod + mod) % mod); write_char('\n'); } inline void solve() { R(n), R(m); for (int i = 1; i <= n; ++i) R(a[i]), add(a[i], a[i - 1]); for (int i = 1; i <= n; ++i) num[i] = (num[i - 1] + (long long)i * (i - 1)) % mod; build(1, 0, n); while (m--) { R(type); if (type == 1) R(l), R(r), R(x), modify(l, r, x); else R(l), R(r), query(l, r); } } // #undef int int main() { //freopen("in.in", "r", stdin); //freopen("out1.out", "w", stdout); solve(); flush(); return 0; }