HDU 6562 Lovers 2018CCPC-吉林站 线段树(超硬核)

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6562

题意

给n个空串和m个操作,操作有两种:

  • $wrap:l,r,d,将[l,r]所有的串首尾都加上一个d,例如33,首尾加个5就变成5335.$
  • $query:l,r,输出\sum_{i=l}^{r}a[i]$

思路

看到更新和查询操作,就知道这题百分之九十九是线段树了,然后看到数据量很大,就知道要懒惰标记了。

所有我们建一棵线段树维护什么呢?

  • 首先就是[l,r]的区间和sum。$sum=\sum_{i=l}^{r}a[i].$
    • 字符串首尾增加字符,需要这段数字的长度,所以还需要len来存。$len=\sum_{i=l}^r10^{len(a[i])}.$
    • 剩下就是懒惰标记了,维护字符串首更新的值lazl,维护字符串尾更新的值lazr,还有一个lazlen,来维护长度的变化。

所以我们有下面的树:

1
2
3
4
5
struct Tree{
int l, r;
long long sum, len;
long long lazl, lazr, lazlen;
};

线段树有建树、区间更新、区间查询三种操作,当然还有标记上传、下传等。

  • 建树略。
1
2
3
4
5
6
7
8
9
10
11
12
13
void build(int u, int l, int r) {
t[u].l = l; t[u].r = r;
t[u].sum = t[u].lazl = t[u].lazr = 0;
t[u].lazlen = 1;
if(l == r) {
t[u].len = 1;
return ;
}
int m = (l + r) >> 1;
build(lc, l, m);
build(rc, m + 1, r);
push_up(u); // 标记上传
}
  • 区间查询略。
1
2
3
4
5
6
7
8
9
10
ll query(int u, int ql, int qr) {
if(ql <= t[u].l && t[u].r <= qr) {
return t[u].sum;
}
push_down(u); // 标记下传
ll ans = 0;
if(ql <= mid) ans = (ans + query(lc, ql, qr)) % mod;
if(qr > mid) ans = (ans + query(rc, ql, qr)) % mod;
return ans;
}
  • 最难的就是区间更新,希望读者能手动自己写一写。

首先给出Code:

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
inline void push_up(int u) {
t[u].sum = (t[lc].sum + t[rc].sum) % mod;
t[u].len = (t[lc].len + t[rc].len) % mod;
}

inline void push_down(int u) {
if(t[u].lazlen == 1) return ;

t[lc].sum = (t[lc].sum * t[u].lazlen % mod + (mid - t[u].l + 1) * t[u].lazr % mod + t[u].lazl * t[lc].len % mod * t[u].lazlen % mod) % mod;
t[rc].sum = (t[rc].sum * t[u].lazlen % mod + (t[u].r - mid) * t[u].lazr % mod + t[u].lazl * t[rc].len % mod * t[u].lazlen % mod) % mod;

t[lc].len = t[lc].len * t[u].lazlen % mod * t[u].lazlen % mod;
t[rc].len = t[rc].len * t[u].lazlen % mod * t[u].lazlen % mod;

t[lc].lazl = (t[lc].lazl + t[u].lazl * t[lc].lazlen) % mod;
t[rc].lazl = (t[rc].lazl + t[u].lazl * t[rc].lazlen) % mod;

t[lc].lazr = (t[lc].lazr * t[u].lazlen + t[u].lazr) % mod;
t[rc].lazr = (t[rc].lazr * t[u].lazlen + t[u].lazr) % mod;

t[lc].lazlen = t[lc].lazlen * t[u].lazlen % mod;
t[rc].lazlen = t[rc].lazlen * t[u].lazlen % mod;

t[u].lazl = t[u].lazr = 0;
t[u].lazlen = 1;
}

void modify(int u, int ql, int qr, int v) {
if(ql <= t[u].l && t[u].r <= qr) {
t[u].sum = (t[u].sum * 10 + (t[u].r - t[u].l + 1) * v + t[u].len * 10 * v) % mod;
t[u].len = t[u].len * 100 % mod;
t[u].lazl = (t[u].lazl + t[u].lazlen * v) % mod;
t[u].lazr = (t[u].lazr * 10 + v) % mod;
t[u].lazlen = t[u].lazlen * 10 % mod;
return ;
}
push_down(u);
if(ql <= mid) modify(lc, ql, qr, v);
if(qr > mid) modify(rc, ql, qr, v);
push_up(u);
}

$区间更新:$

  • sum:首先原数字sum乘10给新增数字留一个尾位,由于是区间内每一个数都要加上d,所以加上(r-l+1)*d,还有还剩下一个首位,只需加上sum的长度len * 10 * d即可。
  • len:加了两个是数字,所以乘10^2即可。
  • lazl:原来的lazl加上新增的,为维护长度的lazlen*d。
  • lazr:在末尾在加一个v,为原来的lazr*10+v。
  • lazlen:新增两个数字,每次只需乘1个10就行了,需要用的时候乘两个lazlen即可。

懒惰标记下传(可以直接手操一波):

  • 孩子的sum:中间(孩子的sum乘父亲的lazlen)加上左边(父亲的lazr乘父亲的lazlen乘孩子的长度len)再加上右边(孩子的区间长度乘父亲的lazr)。
  • 孩子的len:孩子的len乘父亲的lazlen乘父亲的lazlen。
  • 孩子的lazl:孩子的lazl加上父亲的lazl乘上孩子的lazlen。
  • 孩子的lazr:孩子的lazr乘父亲的lazlen+父亲的lazr。
  • 孩子的lazlen:孩子的lazlen乘父亲的lazlen。
  • 最后初始化父亲的懒惰变量。

线段树代码量大不是没有原因的,但这一题还永远不是尽头!

Code(2012MS)

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
##include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<int, int> pdd;

##define INF 0x3f3f3f3f
##define lowbit(x) x & (-x)
##define mem(a, b) memset(a , b , sizeof(a))
##define FOR(i, x, n) for(int i = x;i <= n; i++)

// const ll mod = 998244353;
const ll mod = 1e9 + 7;
// const double eps = 1e-6;
// const double PI = acos(-1);
// const double R = 0.57721566490153286060651209;

const int N = 1e5 + 10;
##define lc u << 1
##define rc u << 1 1
##define mid (t[u].l + t[u].r) / 2

struct Node {
int l, r;
ll sum, len;
ll lazl, lazr, lazlen;
}t[N << 2];

inline void push_up(int u) {
t[u].sum = (t[lc].sum + t[rc].sum) % mod;
t[u].len = (t[lc].len + t[rc].len) % mod;
}

inline void push_down(int u) {
if(t[u].lazlen == 1) return ;

t[lc].sum = (t[lc].sum * t[u].lazlen % mod + (mid - t[u].l + 1) * t[u].lazr % mod + t[u].lazl * t[lc].len % mod * t[u].lazlen % mod) % mod;
t[rc].sum = (t[rc].sum * t[u].lazlen % mod + (t[u].r - mid) * t[u].lazr % mod + t[u].lazl * t[rc].len % mod * t[u].lazlen % mod) % mod;

t[lc].len = t[lc].len * t[u].lazlen % mod * t[u].lazlen % mod;
t[rc].len = t[rc].len * t[u].lazlen % mod * t[u].lazlen % mod;

t[lc].lazl = (t[lc].lazl + t[u].lazl * t[lc].lazlen) % mod;
t[rc].lazl = (t[rc].lazl + t[u].lazl * t[rc].lazlen) % mod;

t[lc].lazr = (t[lc].lazr * t[u].lazlen + t[u].lazr) % mod;
t[rc].lazr = (t[rc].lazr * t[u].lazlen + t[u].lazr) % mod;

t[lc].lazlen = t[lc].lazlen * t[u].lazlen % mod;
t[rc].lazlen = t[rc].lazlen * t[u].lazlen % mod;

t[u].lazl = t[u].lazr = 0;
t[u].lazlen = 1;
}

void build(int u, int l, int r) {
t[u].l = l; t[u].r = r;
t[u].sum = t[u].lazl = t[u].lazr = 0;
t[u].lazlen = 1;
if(l == r) {
t[u].len = 1;
return ;
}
int m = (l + r) >> 1;
build(lc, l, m);
build(rc, m + 1, r);
push_up(u);
}

void modify(int u, int ql, int qr, int v) {
if(ql <= t[u].l && t[u].r <= qr) {
t[u].sum = (t[u].sum * 10 + (t[u].r - t[u].l + 1) * v + t[u].len * 10 * v) % mod;
t[u].len = t[u].len * 100 % mod;
t[u].lazl = (t[u].lazl + t[u].lazlen * v) % mod;
t[u].lazr = (t[u].lazr * 10 + v) % mod;
t[u].lazlen = t[u].lazlen * 10 % mod;
return ;
}
push_down(u);
if(ql <= mid) modify(lc, ql, qr, v);
if(qr > mid) modify(rc, ql, qr, v);
push_up(u);
}

ll query(int u, int ql, int qr) {
if(ql <= t[u].l && t[u].r <= qr) {
return t[u].sum;
}
push_down(u);
ll ans = 0;
if(ql <= mid) ans = (ans + query(lc, ql, qr)) % mod;
if(qr > mid) ans = (ans + query(rc, ql, qr)) % mod;
return ans;
}

void solve()
{
int _, Case = 1;
cin >> _;
while(_--) {
cout << "Case " << Case++ << ":" << endl;
int n, m;
cin >> n >> m;
build(1, 1, n);
while(m--) {
int l, r;
string s; cin >> s >> l >> r;
if(s == "wrap") {
int v; cin >> v;
modify(1, l, r, v);
}
else {
cout << query(1, l, r) << endl;
}
}
}
}

signed main() {
ios_base::sync_with_stdio(false);
//cin.tie(nullptr);
//cout.tie(nullptr);
##ifdef FZT_ACM_LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
signed test_index_for_debug = 1;
char acm_local_for_debug = 0;
do {
if (acm_local_for_debug == '$') exit(0);
if (test_index_for_debug > 20)
throw runtime_error("Check the stdin!!!");
auto start_clock_for_debug = clock();
solve();
auto end_clock_for_debug = clock();
cout << "Test " << test_index_for_debug << " successful" << endl;
cerr << "Test " << test_index_for_debug++ << " Run Time: "
<< double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
cout << "--------------------------------------------------" << endl;
} while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
##else
solve();
##endif
return 0;
}

本文作者:jujimeizuo
本文地址https://blog.jujimeizuo.cn/2020/12/04/hdu-6562/
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0 协议。转载请注明出处!