题目链接:牛牛的Link Power II

题意:给你一个只含$0$和$1$的串,定义串的$Link$值为串中两个的$1$之间的距离的和,$(u,v)$和$(v,u)$被看认为是同一对,有$m$次操作,每次操作可以把串中某个$1$变为$0$,或者把某个$0$变为$1$,求一开始和每次操作后串的$Link$值。

思路:线段树,维护区间内$1$的数量$cnt、$区间的$Link$值$w、$区间内所有的$1$到区间左边界的距离之和$dl、$区间内所有的$1$到区间右边界的距离之和$dr$,查询时只要输出$tree[1].w$即可,修改时只用改变区间内$1$的$cnt$,然后进行区间合并,区间合并的公式画个图推导一下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;

typedef long long ll;

const int N = 100010;
const ll mod = 1000000007;

struct node {
int l, r;
ll dl, dr, w, cnt;
};

node tree[4 * N];
char s[N];
int n, q;

void update(int k)
{
tree[k].cnt = tree[2 * k].cnt + tree[2 * k + 1].cnt;
ll t = tree[2 * k].cnt * tree[2 * k + 1].dl + tree[2 * k + 1].cnt * tree[2 * k].dr;
tree[k].w = tree[2 * k].w + tree[2 * k + 1].w + t + tree[2 * k].cnt * tree[2 * k + 1].cnt;
tree[k].dl = tree[2 * k].dl + tree[2 * k + 1].dl + tree[2 * k + 1].cnt * (tree[2 * k].r - tree[2 * k].l + 1);
tree[k].dr = tree[2 * k].dr + tree[2 * k + 1].dr + tree[2 * k].cnt * (tree[2 * k + 1].r - tree[2 * k + 1].l + 1);
}

void build(int k, int lef, int rig)
{
tree[k].l = lef, tree[k].r = rig;
if (tree[k].l == tree[k].r) {
tree[k].w = tree[k].dl = tree[k].dr = 0;
if ('1' == s[tree[k].l]) tree[k].cnt = 1;
else tree[k].cnt = 0;
return;
}
int mid = (lef + rig) / 2;
build(k * 2, lef, mid);
build(k * 2 + 1, mid + 1, rig);
update(k);
}

void change_point(int k, int x, int y)
{
if (tree[k].l == tree[k].r) {
tree[k].cnt = y;
return;
}
int mid = (tree[k].l + tree[k].r) / 2;
if (x <= mid) change_point(2 * k, x, y);
else change_point(2 * k + 1, x, y);
update(k);
}

int main()
{
scanf("%d%s", &n, s + 1);
build(1, 1, n);
printf("%lld\n", tree[1].w % mod);
scanf("%d", &q);
while (q--) {
int kd, pos;
scanf("%d%d", &kd, &pos);
change_point(1, pos, 1 == kd ? 1 : 0);
printf("%lld\n", tree[1].w % mod);
}
return 0;
}