2020牛客寒假算法基础集训营3 G 牛牛的Link Power II(线段树)

题目链接

题目描述

牛牛有一颗大小为n的神奇Link-Cut 数组,数组上的每一个节点都有两种状态,一种为link状态,另一种为cut状态。数组上任意一对处于link状态的无序点对(即(u,v)和(v,u)被认为是同一对)会产生dis(u,v)的link能量,dis(u,v)为数组上u到v的距离。

我们定义整个数组的Link能量为所有处于link状态的节点产生的link能量之和。

一开始数组上每个节点的状态将由一个长度大小为n的01串给出,'1'表示Link状态,'0'表示Cut状态。

牛牛想要知道一开始,以及每次操作之后整个数组的Link能量,为了避免这个数字过于庞大,你只用输出答案对1e9+7取余后的结果即可。

思路

 把一个大区间分成两个小区间计算,例如:11011111,可以分开成1101和1111,那么大区间的Link值就是左边区间内的Link加右区间的Link,再加上左边区间和右边区间互相的贡献,所以可以用线段树维护一下每个区间内的Link值,左右区间互相的贡献也很好计算。

设他们的下标从1开始,那么就有

1 1 0 1 1 1 1 1
1 2 3 4 5 6 7 8

可以看出,左右两边互相的贡献就是

[(5-1)+(6-1)+(7-1)+(8-1)] +[(5-2)+(6-2)+(7-2)+(8-2)]+[(5-4)+(6-4)+(7-4)+(8-4)]

把左边区间的点的坐标和右边区间点的坐标提取出来,可以发现,结果就是

右区间所有为1 的点的下标之和rpossum乘以左边为1 的点的个数lcnt,减去左区间所有为1 的点的下标和lpossum乘以右区间为1 的点的个数rcnt,即rpossum\times lcnt-lpossum\times rcnt

因此只需要线段树去维护各区间内的为1 的点的个数和为1 的点的下标和即可,每次查询的时候更新完直接输出根节点的sum值即可(注意取模)

#include <bits/stdc++.h>
#pragma warning (disable:4996)
#pragma warning (disable:6031)
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
const int mod = 1e9 + 7;
char s[N];
struct p {
	ll l, r, possum, cnt, sum;
};
struct SegementTree {
	p c[N * 4];
	SegementTree() {
		mem(c, 0);
	}
	void build(ll l, ll r, ll k) {
		c[k].l = l;
		c[k].r = r;
		c[k].possum = 0;
		c[k].sum = 0;
		c[k].cnt = 0;
		if (l == r) {
			if (s[l - 1] == '0')return ;
			c[k].cnt = 1;
			c[k].possum = l;
			c[k].sum = 0;
			return ;
		}
		ll mid = (l + r) / 2;
		build(l, mid, k << 1);
		build(mid + 1, r, k << 1 | 1);
		c[k].cnt = c[k << 1].cnt + c[k << 1 | 1].cnt;
		c[k].possum = c[k << 1].possum + c[k << 1 | 1].possum;
		c[k].sum = c[k << 1].sum + c[k << 1 | 1].sum;
		c[k].sum %= mod;
		c[k].sum += c[k << 1 | 1].possum * c[k << 1].cnt % mod - c[k << 1].possum * c[k << 1 | 1].cnt % mod;
		c[k].sum = (c[k].sum + mod) % mod;
	}
	void update1(ll ind, ll k) {
		// 0->1
		if (c[k].l == c[k].r) {
			c[k].cnt = 1;
			c[k].possum = c[k].l;
			c[k].sum = 0;
			return ;
		}
		ll mid = c[k].l + c[k].r;
		mid /= 2;
		if (ind <= mid)update1(ind, k << 1);
		else update1(ind, k << 1 | 1);
		c[k].possum = c[k << 1].possum + c[k << 1 | 1].possum;
		c[k].cnt = c[k << 1].cnt + c[k << 1 | 1].cnt;
		c[k].sum = c[k << 1].sum + c[k << 1 | 1].sum;
		c[k].sum %= mod;
		c[k].sum += c[k << 1 | 1].possum * c[k << 1].cnt % mod - c[k << 1].possum * c[k << 1 | 1].cnt % mod;
		c[k].sum = (c[k].sum + mod) % mod;
	}
	void update2(ll ind, ll k) {
		// 1->0
		if (c[k].l == c[k].r) {
			c[k].cnt = 0;
			c[k].possum = 0;
			c[k].sum = 0;
			return ;
		}
		ll mid = c[k].l + c[k].r;
		mid /= 2;
		if (ind <= mid)update2(ind, k << 1);
		else update2(ind, k << 1 | 1);
		c[k].possum = c[k << 1].possum + c[k << 1 | 1].possum;
		c[k].cnt = c[k << 1].cnt + c[k << 1 | 1].cnt;
		c[k].sum = c[k << 1].sum + c[k << 1 | 1].sum;
		c[k].sum %= mod;
		c[k].sum += c[k << 1 | 1].possum * c[k << 1].cnt % mod - c[k << 1].possum * c[k << 1 | 1].cnt % mod;
		c[k].sum = (c[k].sum + mod) % mod;
	}
};
SegementTree st;
int main()
{
	int n;
	scanf("%d", &n);
	scanf("%s", s);
	st.build(1, n, 1);
	printf("%lld\n", st.c[1].sum);
	int q;
	scanf("%d", &q);
	for (int i = 1; i <= q; i++) {
		ll a, b;
		scanf("%lld %lld", &a, &b);
		if (a == 1) {
			st.update1(b, 1);
		}
		else st.update2(b, 1);
		printf("%lld\n", st.c[1].sum);
	}
	return 0;
}
发布了165 篇原创文章 · 获赞 11 · 访问量 9624

猜你喜欢

转载自blog.csdn.net/weixin_43701790/article/details/104261262