HDU6562 Lovers (线段树 懒标记下放)

题目链接: Lovers

大致题意

给定 n n n个字符串, s 1 , s 2 , … , s n s_1, s_2, …, s_n s1,s2,,sn 初始均为空串.

m m m次如下操作:

warp l r d: 其中 d d d是一个数字字符, 表示对 s [ l : r ] s[l:r] s[l:r]的所有字符串开头和结尾都加上字符 d d d.

query l r 询问 ∑ i − l r v a l u e s ( s i ) \displaystyle \sum_{i - l}^{r}values(s_i) ilrvalues(si). 其中 v a l u e s ( s t r ) values(str) values(str)为字符串 s t r str str所表示的十进制数值. 规定: 空串的值为0.

解题思路

线段树

我们考虑如果对于一个十进制数值为 v a l val val, 长度为 l e n len len的字符串首尾加上字符 d d d.

此时 v a l = v a l ∗ 10 + d + d ∗ 1 0 l e n + 1 val = val * 10 + d + d * 10^{len+1} val=val10+d+d10len+1. 我们用加号作为分割来看每一部分:
​ ① v a l ∗ 10 val * 10 val10, 是由于最右边增加了一位, 因此整体扩大 10 10 10.
​ ② d d d, 最右边增加的数值
​ ③ d ∗ 1 0 l e n + 1 d * 10^{len + 1} d10len+1 本身在长度为 l e n len len的串前增加一个 d d d, 那么增加的数值为 d ∗ 1 0 l e n d * 10^{len} d10len, 但是最右侧还增加了一位, 因此等价于在长度为 l e n + 1 len + 1 len+1的串前增加了一位 d d d. 因此增加的数值为 d ∗ 1 0 l e n + 1 d*10^{len+1} d10len+1.

其实我们也可以写作 v a l = ( v a l + d ∗ 1 0 l e n ) ∗ 10 + d val = (val + d * 10^{len}) * 10 + d val=(val+d10len)10+d

可以这样理解: 我们先拼接了左侧的 d d d, 然后再拼接右侧 d d d, 因此原先的结果需要 × 10 \times 10 ×10.


我们考虑用线段树维护修改操作: 由上文得出我们需要维护 区间和 s u m sum sum, 区间字串长度和 l e n len len.

由于我们保存长度, 是为了得到 1 0 l e n 10^{len} 10len, 因此我们也可以直接保存成 1 0 l e n 10^{len} 10len的形式. (便于后续维护书中信息)

为了能够正确的维护信息, 我们还需要三个懒标记: 在串开头增加的数字 l a l lal lal, 在串结尾增加的数字 l a r lar lar, 串的新增长度 l a l e n lalen lalen.

下放方式参考代码pushdown()

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, mod = 1E9 + 7;
struct node {
	int l, r;
	int sum, len;
	int lal, lar, lalen;
}t[N << 2];
void pushdown(node& op, int lal, int lar, int lalen) { // lalen表示的是单侧增加的长度
	const int arealen = op.r - op.l + 1;
	int qaq1 = 1ll * lal * op.len % mod; // 表示拼接左边部分产生的贡献
	int qaq2 = 1ll * lar * arealen % mod; // 表示拼接右边部分产生的贡献

	op.sum = (1ll * (op.sum + qaq1) % mod * lalen + qaq2) % mod; // 先拼接完左边, 然后整体左移后, 再加上右边的贡献.
	op.len = 1ll * op.len * lalen % mod * lalen % mod; // 注意长度变化, 左边的和右边的都要算一次.

	op.lal = (op.lal + 1ll * lal * op.lalen) % mod; // 我们要把当前的lal拼接到串头, 而真实的区间长度是len * op.lalen.
	op.lar = (1ll * op.lar * lalen + lar) % mod; 
	op.lalen = 1ll * op.lalen * lalen % mod;
}
void pushdown(int x) {
	if (!t[x].lal and !t[x].lar and t[x].lalen == 1) return;
	pushdown(t[x << 1], t[x].lal, t[x].lar, t[x].lalen), pushdown(t[x << 1 | 1], t[x].lal, t[x].lar, t[x].lalen);
	t[x].lal = t[x].lar = 0, t[x].lalen = 1;
}
void pushup(int x) {
	t[x].sum = (t[x << 1].sum + t[x << 1 | 1].sum) % mod;
	t[x].len = (t[x << 1].len + t[x << 1 | 1].len) % mod;
}

void build(int l, int r, int x = 1) {
	t[x] = { l, r, 0, r - l + 1, 0, 0, 1 };
	if (l == r) return;
	int mid = l + r >> 1;
	build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
}

void modify(int l, int r, int c, int x = 1) {
	if (l <= t[x].l and r >= t[x].r) {
		pushdown(t[x], c, c, 10);
		return;
	}
	pushdown(x);
	int mid = t[x].l + t[x].r >> 1;
	if (l <= mid) modify(l, r, c, x << 1);
	if (r > mid) modify(l, r, c, x << 1 | 1);
	pushup(x);
}

int ask(int l, int r, int x = 1) {
	if (l <= t[x].l and r >= t[x].r) return t[x].sum;
	pushdown(x);
	int mid = t[x].l + t[x].r >> 1;
	int res = 0;
	if (l <= mid) res = ask(l, r, x << 1);
	if (r > mid) res += ask(l, r, x << 1 | 1);
	return res % mod;
}
int main()
{
	int T; cin >> T;
	rep(tt, T) {
		printf("Case %d:\n", tt);

		int n, m; scanf("%d %d", &n, &m);
		build(1, n);

		rep(i, m) {
			char s[10]; scanf("%s", s);
			if (*s == 'w') {
				int l, r, c; scanf("%d %d %d", &l, &r, &c);
				modify(l, r, c);
			}
			else {
				int l, r; scanf("%d %d", &l, &r);
				printf("%d\n", ask(l, r));
			}
		}
	}

	return 0;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值