题目链接: 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)
i−l∑rvalues(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=val∗10+d+d∗10len+1. 我们用加号作为分割来看每一部分:
①
v
a
l
∗
10
val * 10
val∗10, 是由于最右边增加了一位, 因此整体扩大
10
10
10.
②
d
d
d, 最右边增加的数值
③
d
∗
1
0
l
e
n
+
1
d * 10^{len + 1}
d∗10len+1 本身在长度为
l
e
n
len
len的串前增加一个
d
d
d, 那么增加的数值为
d
∗
1
0
l
e
n
d * 10^{len}
d∗10len, 但是最右侧还增加了一位, 因此等价于在长度为
l
e
n
+
1
len + 1
len+1的串前增加了一位
d
d
d. 因此增加的数值为
d
∗
1
0
l
e
n
+
1
d*10^{len+1}
d∗10len+1.
其实我们也可以写作 v a l = ( v a l + d ∗ 1 0 l e n ) ∗ 10 + d val = (val + d * 10^{len}) * 10 + d val=(val+d∗10len)∗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;
}