题目传送门
sol:可以用线段树来维护,线段树的节点除了标配的$l$和$r$同时记录该区间$link$的个数记为$cnt$,该区间$link$点的和记为$sum$,该区间题目中所谓的能量记为$dis$。然后$cnt$和$sum$就直接两个儿子相加就好,$dis$还要另外加上$segTree[rs].sum * segTree[ls].cnt - segTree[ls].sum * segTree[rs].cnt$这部分。
- 线段树
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 1e5 + 10; const int MOD = 1e9 + 7; struct Node { int l, r; int cnt; int sum, dis; } segTree[4 * MAXN]; void push_up(int i) { int ls = i << 1, rs = i << 1 | 1; segTree[i].cnt = segTree[ls].cnt + segTree[rs].cnt; segTree[i].sum = (segTree[ls].sum + segTree[rs].sum) % MOD; segTree[i].dis = (1LL * segTree[rs].sum * segTree[ls].cnt - 1LL * segTree[ls].sum * segTree[rs].cnt) % MOD; segTree[i].dis = (1LL * MOD + segTree[i].dis + segTree[ls].dis + segTree[rs].dis) % MOD; } void build(int i, int l, int r) { segTree[i].l = l; segTree[i].r = r; if (l == r) { int k = getchar() ^ '0'; segTree[i].cnt = k; segTree[i].sum = k * l; return; } int mid = l + r >> 1; build(i << 1, l, mid); build(i << 1 | 1, mid + 1, r); push_up(i); } void update(int i, int k, int data) { if (segTree[i].l == segTree[i].r) { segTree[i].cnt = data; segTree[i].sum = data * segTree[i].l; return; } int mid = segTree[i].l + segTree[i].r >> 1; if (k <= mid) update(i << 1, k, data); else update(i << 1 | 1, k, data); push_up(i); } int main() { int n; scanf("%d%*c", &n); build(1, 1, n); printf("%d\n", segTree[1].dis); int q; scanf("%d", &q); while (q--) { int op, k; scanf("%d%d", &op, &k); if (op == 1) update(1, k, 1); else update(1, k, 0); printf("%d\n", segTree[1].dis); } return 0; }
这个线段树如果要查询某个区间的dis其实不大好写,但是题目中所有的查询都是针对整棵树,也就是segTree[1]。所以省掉了询问函数。