牛客竞赛数据结构专题班树状数组、线段树练习题
二分题解
二分答案,用差分+前缀和check
题意:
m
m
m 次操作,每次区间
−
d
-d
−d
每次操作前判断最小值是否
>
=
d
>=d
>=d
区间修改,维护区间最小值
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 5;
const int inf = 0x3f3f3f3f;
ll tree[MAXN << 2], mark[MAXN << 2], n, m, A[MAXN];
void push_down(int p, int len)
{
if(!mark[p]) return;
tree[p << 1] -= mark[p];
mark[p << 1] += mark[p];
tree[p << 1 | 1] -= mark[p];
mark[p << 1 | 1] += mark[p];
mark[p] = 0;
}
void build(int p = 1, int cl = 1, int cr = n)
// 函数定义参数的时候赋值,如果没传参数,就是赋予的值,否则就是传递的值
{
if (cl == cr) { tree[p] = A[cl]; return; }
int mid = (cl + cr) >> 1;
build(p << 1, cl, mid);
build(p << 1 | 1, mid + 1, cr);
tree[p] = min(tree[p << 1], tree[p << 1 | 1]);
}
ll query(int l, int r, int p = 1, int cl = 1, int cr = n)
{
if (cl >= l && cr <= r) return tree[p];
push_down(p, cr - cl + 1);
ll mid = (cl + cr) >> 1, ans = inf;
if (mid >= l) ans = min(query(l, r, p << 1, cl, mid), ans);
if (mid < r) ans = min(query(l, r, p << 1 | 1, mid + 1, cr), ans);
return ans;
}
void update(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
if (cl >= l && cr <= r) { tree[p] -= d, mark[p] += d; return; }
// 只有整个区间被修改的节点才会有懒惰标记
push_down(p, cr - cl + 1);
int mid = (cl + cr) >> 1;
if (mid >= l) update(l, r, d, p << 1, cl, mid);
if (mid < r) update(l, r, d, p << 1 | 1, mid + 1, cr);
tree[p] = min(tree[p << 1], tree[p << 1 | 1]);
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> A[i];
build();
int ans = 0;
for(int i = 1; i <= m; ++i)
{
int d, l, r;
cin >> d >> l >> r;
if(query(l, r) >= d){
update(l, r, d);
}
else {
ans = i;break;
}
}
if(ans) cout << -1 << endl;
cout << ans;
return 0;
}
数据结构
题意:
四个操作
- 查询区间和
- 查询区间平方和
- 区间乘 x x x
- 区间加 x x x
思路:
这也是一道维护区间加分和乘法的题目,但是多了一查询方式,查询区间所有数的平方的和。
区间和容易维护,那区间平方和和立方和呢?
维护区间平方和的题目
难度比这个小一点,没有区间乘的更新
区间平方和的维护:
设
s
u
m
1
=
a
1
+
a
2
+
a
3
+
.
.
.
+
a
n
s
u
m
2
=
a
1
2
+
a
2
2
+
a
3
2
+
.
.
.
+
a
n
2
sum_1=a_1+a_2+a_3+...+a_n\\ sum_2=a_1^2+a_2^2+a_3^2+...+a_n^2\\
sum1=a1+a2+a3+...+ansum2=a12+a22+a32+...+an2
区间加
x
x
x
(
a
1
+
x
)
2
+
(
a
2
+
x
)
2
+
(
a
3
+
x
)
2
+
.
.
.
+
(
a
n
+
x
)
2
=
a
1
2
+
2
a
1
x
+
x
2
+
a
2
2
+
2
a
2
x
+
x
2
+
a
3
+
2
a
3
x
+
x
3
+
.
.
.
+
a
n
2
+
2
a
n
x
+
x
2
=
(
a
1
2
+
a
2
2
+
a
3
2
+
.
.
.
+
a
n
2
)
+
2
x
(
a
1
+
a
2
+
a
3
+
.
.
.
+
a
n
)
+
n
x
2
=
s
u
m
2
+
2
x
∗
s
u
m
1
+
n
x
2
(a_1+x)^2+(a_2+x)^2+(a_3+x)^2+...+(a_n+x)^2\\ = a_1^2+2a_1x+x^2+a_2^2+2a_2x+x^2+a^3+2a_3x+x^3+...+a_n^2+2a_nx+x^2\\ = (a_1^2+a_2^2+a_3^2+...+a_n^2)+2x(a_1+a_2+a_3+...+a_n)+nx^2\\ =sum_2+2x*sum_1+nx^2
(a1+x)2+(a2+x)2+(a3+x)2+...+(an+x)2=a12+2a1x+x2+a22+2a2x+x2+a3+2a3x+x3+...+an2+2anx+x2=(a12+a22+a32+...+an2)+2x(a1+a2+a3+...+an)+nx2=sum2+2x∗sum1+nx2
区间乘
y
y
y
(
y
a
1
)
2
+
(
y
a
2
)
2
+
(
y
a
3
)
2
+
.
.
.
+
(
y
a
n
)
2
=
y
2
(
a
1
2
+
a
2
2
+
a
3
2
+
.
.
.
+
a
n
2
)
(ya_1)^2+(ya_2)^2+(ya_3)^2+...+(ya_n)^2\\ =y^2(a_1^2+a_2^2+a_3^2+...+a_n^2)
(ya1)2+(ya2)2+(ya3)2+...+(yan)2=y2(a12+a22+a32+...+an2)
区间乘
y
+
x
y+x
y+x
(
y
a
1
+
x
)
2
+
(
y
a
2
+
x
)
2
+
(
y
a
3
+
x
)
2
+
.
.
.
+
(
y
a
n
+
x
)
2
=
y
2
a
1
2
+
2
a
1
x
y
+
x
2
+
y
2
a
2
2
+
2
a
2
x
y
+
x
2
+
y
2
a
3
+
2
a
3
x
y
+
x
3
+
.
.
.
+
y
2
a
n
2
+
2
a
n
x
y
+
x
2
=
y
2
(
a
1
2
+
a
2
2
+
a
3
2
+
.
.
.
+
a
n
2
)
+
2
x
y
(
a
1
+
a
2
+
a
3
+
.
.
.
+
a
n
)
+
n
x
2
=
y
2
s
u
m
2
+
2
x
y
∗
s
u
m
1
+
n
x
2
(ya_1+x)^2+(ya_2+x)^2+(ya_3+x)^2+...+(ya_n+x)^2\\ = y^2a_1^2+2a_1xy+x^2+y^2a_2^2+2a_2xy+x^2+y^2a^3+2a_3xy+x^3+...+y^2a_n^2+2a_nxy+x^2\\ = y^2(a_1^2+a_2^2+a_3^2+...+a_n^2)+2xy(a_1+a_2+a_3+...+a_n)+nx^2\\ =y^2sum_2+2xy*sum_1+nx^2
(ya1+x)2+(ya2+x)2+(ya3+x)2+...+(yan+x)2=y2a12+2a1xy+x2+y2a22+2a2xy+x2+y2a3+2a3xy+x3+...+y2an2+2anxy+x2=y2(a12+a22+a32+...+an2)+2xy(a1+a2+a3+...+an)+nx2=y2sum2+2xy∗sum1+nx2
注意
n
n
n 是区间长度
然后就可以愉快的写
u
p
d
a
t
e
update
update 和
p
u
s
h
d
o
w
n
pushdown
pushdown 函数啦~
(这个题就是洛谷线段树模板二和上边维护区间平方和的结合体
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
ll n, m;
ll a[maxn];
struct node
{
ll sum, add, mul;
}t1[maxn << 2], t2[maxn << 2];
inline void pushdown(int p, int len)
{
//if(t1[p].mark != t2[p].mark) return; // 测试一下,显然 t1和t2 的 mark是完全一样的,不一样会因为这句wa掉
ll x = t2[p].add, y = t2[p].mul;// 写成t1也一样
// 先维护平方和,再维护和!!
// 区间平方和
t2[p << 1].add = t2[p << 1].add * y + x;
t2[p << 1].mul = t2[p << 1].mul * y;
t2[p << 1].sum = t2[p << 1].sum * y * y + 2 * x * y * t1[p << 1].sum + (len - len / 2) * x * x;
t2[p << 1 | 1].add = t2[p << 1 | 1].add * y + x;
t2[p << 1 | 1].mul = t2[p << 1 | 1].mul * y;
t2[p << 1 | 1].sum = t2[p << 1 | 1].sum * y * y + 2 * x * y * t1[p << 1 | 1].sum + (len / 2) * x * x;
// 区间和
t1[p << 1].add = t1[p << 1].add * y + x;
t1[p << 1].mul = t1[p << 1].mul * y;
t1[p << 1].sum = t1[p << 1].sum * y + (len - len / 2) * x;
t1[p << 1 | 1].add = t1[p << 1 | 1].add * y + x;
t1[p << 1 | 1].mul = t1[p << 1 | 1].mul * y;
t1[p << 1 | 1].sum = t1[p << 1 | 1].sum * y + (len / 2) * x;
t1[p].add = t2[p].add = 0;
t1[p].mul = t2[p].mul = 1;
}
inline void build(int p = 1, int cl = 1, int cr = n)
{
t1[p].add = t2[p].add = 0;t1[p].mul = t2[p].mul = 1;
if(cl == cr){
t1[p].sum = a[cl]; t2[p].sum= a[cl] * a[cl];return;
}
int mid = (cl + cr) >> 1;
build(p << 1, cl, mid);
build(p << 1 | 1, mid + 1, cr);
t1[p].sum = t1[p << 1].sum + t1[p << 1 | 1].sum;
t2[p].sum = t2[p << 1].sum + t2[p << 1 | 1].sum;
}
inline ll query(node tree[], int l, int r, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r) return tree[p].sum;
int mid = (cl + cr) >> 1;
if(tree[p].add || tree[p].mul != 1) pushdown(p, cr - cl + 1);
ll ans = 0;
if(mid >= l) ans += query(tree, l, r, p << 1, cl, mid);
if(mid < r) ans += query(tree, l, r, p << 1 | 1, mid + 1, cr);
//t1[p].sum = t1[p << 1].sum + t1[p << 1 | 1].sum;
//t2[p].sum = t2[p << 1].sum + t2[p << 1 | 1].sum;
// 不修改值,不用 pushup
return ans;
}
inline void add(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r){
t1[p].add += d; t2[p].add += d;
t2[p].sum += t1[p].sum * (d * 2) + (cr - cl + 1) * (d * d);
t1[p].sum += (cr - cl + 1) * d;
return;
}
if(t1[p].add || t1[p].mul != 1) pushdown(p, cr - cl + 1);
int mid = (cl + cr) >> 1;
if(mid >= l) add(l, r, d, p << 1, cl, mid);
if(mid < r) add(l, r, d, p << 1 | 1, mid + 1, cr);
t1[p].sum = t1[p << 1].sum + t1[p << 1 | 1].sum;
t2[p].sum = t2[p << 1].sum + t2[p << 1 | 1].sum;
}
inline void mul(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r){
t1[p].mul *= d; t2[p].mul *= d;
t1[p].add *= d; t2[p].add *= d;
t1[p].sum *= d; t2[p].sum *= d * d;
return;
}
if(t1[p].add || t1[p].mul != 1) pushdown(p, cr - cl + 1);
int mid = (cl + cr) >> 1;
if(mid >= l) mul(l, r, d, p << 1, cl, mid);
if(mid < r) mul(l, r, d, p << 1 | 1, mid + 1, cr);
t1[p].sum = t1[p << 1].sum + t1[p << 1 | 1].sum;
t2[p].sum = t2[p << 1].sum + t2[p << 1 | 1].sum;
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) cin >> a[i];
build();
while(m--)
{
int op, l, r;
cin >> op >> l >> r;
if(op == 1){
cout << query(t1, l, r) << endl;
}
else if(op == 2){
cout << query(t2, l, r) << endl;
}
else if(op == 3) {
int k;cin >> k;mul(l, r, k);
}
else {
int k;cin >> k;add(l, r, k);
}
}
}
int main()
{
ios::sync_with_stdio(0);
work();
return 0;
}
线段树
题意:
三个操作
- 区间加 x x x
- 区间乘 x x x
- 查询区间所有两个数的乘积之和
思路:
如何维护区间任意两个数的乘积
∑
i
=
l
r
∑
j
=
i
+
1
r
a
i
a
j
⟹
∑
i
=
l
r
a
i
∑
j
=
i
+
1
r
a
j
⟹
∑
i
=
l
r
a
i
∑
j
=
l
r
a
j
−
∑
i
=
l
r
a
i
2
2
⟹
(
∑
i
=
l
r
a
i
)
2
−
∑
i
=
l
r
a
i
2
2
\sum_{i=l}^{r}\sum_{j=i+1}^{r}a_ia_j\Longrightarrow \sum_{i=l}^{r}a_i\sum_{j=i+1}^{r}a_j\Longrightarrow \frac{\sum_{i=l}^{r}a_i\sum_{j=l}^{r}a_j-\sum_{i=l}^{r}a_i^2}{2}\Longrightarrow\frac{(\sum_{i=l}^{r}a_i)^2-\sum_{i=l}^{r}a_i^2}{2}
i=l∑rj=i+1∑raiaj⟹i=l∑raij=i+1∑raj⟹2∑i=lrai∑j=lraj−∑i=lrai2⟹2(∑i=lrai)2−∑i=lrai2
(区间平方和
−
-
− 区间和)/ 2 得到的显然是区间任意两个数的乘积,因此我们维护这两个和即可
和上一个题的差不多
区间加法乘法操作,维护区间和和区间平方和
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
ll mod;
ll n, m;
ll a[maxn];
struct node
{
ll sum, add, mul;
}t1[maxn << 2], t2[maxn << 2];
inline void pushdown(int p, int len)
{
//if(t1[p].mark != t2[p].mark) return; // 测试一下,显然 t1和t2 的 mark是完全一样的,不一样会因为这句wa掉
ll x = t2[p].add % mod, y = t2[p].mul % mod;// 写成t1也一样
// 先维护平方和,再维护和!!
// 区间平方和
t2[p << 1].add = (t2[p << 1].add * y % mod + x) % mod;
t2[p << 1].mul = t2[p << 1].mul * y % mod;
t2[p << 1].sum = ((t2[p << 1].sum * (y * y % mod) % mod + 2 * ((x * y % mod) * t1[p << 1].sum % mod) % mod) % mod + (len - len / 2) * (x * x % mod) % mod) % mod;
t2[p << 1 | 1].add = (t2[p << 1 | 1].add * y % mod + x) % mod;
t2[p << 1 | 1].mul = t2[p << 1 | 1].mul * y % mod;
t2[p << 1 | 1].sum = ((t2[p << 1 | 1].sum * (y * y % mod) % mod + 2 * ((x * y % mod) * t1[p << 1 | 1].sum % mod) % mod) % mod + (len / 2) * (x * x % mod) % mod) % mod;
// 区间和
t1[p << 1].add = (t1[p << 1].add * y % mod + x) % mod;
t1[p << 1].mul = t1[p << 1].mul * y % mod;
t1[p << 1].sum = (t1[p << 1].sum * y % mod + (len - len / 2) * x % mod) % mod;
t1[p << 1 | 1].add = (t1[p << 1 | 1].add * y % mod + x) % mod;
t1[p << 1 | 1].mul = t1[p << 1 | 1].mul * y % mod;
t1[p << 1 | 1].sum = (t1[p << 1 | 1].sum * y % mod + (len / 2) * x % mod) % mod;
t1[p].add = t2[p].add = 0;
t1[p].mul = t2[p].mul = 1;
}
inline void build(int p = 1, int cl = 1, int cr = n)
{
t1[p].add = t2[p].add = 0;t1[p].mul = t2[p].mul = 1;
if(cl == cr){
t1[p].sum = a[cl] % mod; t2[p].sum = a[cl] * a[cl] % mod;return;
}
int mid = (cl + cr) >> 1;
build(p << 1, cl, mid);
build(p << 1 | 1, mid + 1, cr);
t1[p].sum = (t1[p << 1].sum + t1[p << 1 | 1].sum) % mod;
t2[p].sum = (t2[p << 1].sum + t2[p << 1 | 1].sum) % mod;
}
inline ll query(node tree[], int l, int r, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r) return tree[p].sum % mod;
int mid = (cl + cr) >> 1;
if(tree[p].add || tree[p].mul != 1) pushdown(p, cr - cl + 1);
ll ans = 0;
if(mid >= l) ans += query(tree, l, r, p << 1, cl, mid), ans %= mod;
if(mid < r) ans += query(tree, l, r, p << 1 | 1, mid + 1, cr), ans %= mod;
//t1[p].sum = t1[p << 1].sum + t1[p << 1 | 1].sum;
//t2[p].sum = t2[p << 1].sum + t2[p << 1 | 1].sum;
// 不修改值,不用 pushup
return ans;
}
inline void add(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r){
t1[p].add += d; t2[p].add += d; t1[p].add %= mod; t2[p].add %= mod;
t2[p].sum += t1[p].sum * (d * 2) % mod + (cr - cl + 1) * (d * d % mod) % mod; t2[p].sum %= mod;
t1[p].sum += (cr - cl + 1) * d; t1[p].sum %= mod;
return;
}
if(t1[p].add || t1[p].mul != 1) pushdown(p, cr - cl + 1);
int mid = (cl + cr) >> 1;
if(mid >= l) add(l, r, d, p << 1, cl, mid);
if(mid < r) add(l, r, d, p << 1 | 1, mid + 1, cr);
t1[p].sum = (t1[p << 1].sum + t1[p << 1 | 1].sum) % mod;
t2[p].sum = (t2[p << 1].sum + t2[p << 1 | 1].sum) % mod;
}
inline void mul(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r){
t1[p].mul *= d; t2[p].mul *= d; t1[p].mul %= mod; t2[p].mul %= mod;
t1[p].add *= d; t2[p].add *= d; t1[p].add %= mod; t2[p].add %= mod;
t1[p].sum *= d; t2[p].sum *= d * d % mod; t1[p].sum %= mod; t2[p].sum %= mod;
return;
}
if(t1[p].add || t1[p].mul != 1) pushdown(p, cr - cl + 1);
int mid = (cl + cr) >> 1;
if(mid >= l) mul(l, r, d, p << 1, cl, mid);
if(mid < r) mul(l, r, d, p << 1 | 1, mid + 1, cr);
t1[p].sum = (t1[p << 1].sum + t1[p << 1 | 1].sum) % mod;
t2[p].sum = (t2[p << 1].sum + t2[p << 1 | 1].sum) % mod;
}
ll q_pow(ll a, ll n, ll ans = 1){
while(n){
if(n & 1) ans = ans * a % mod; a = a * a % mod; n >>= 1;
}return ans;
}
void work()
{
cin >> n >> m >> mod;
for(int i = 1; i <= n; ++i) cin >> a[i];
build();
while(m--)
{
int op, l, r;
cin >> op >> l >> r;
if(op == 2) {
int k;cin >> k;mul(l, r, k);
}
else if(op == 1){
int k;cin >> k;add(l, r, k);
}
else
{
ll x = query(t1, l, r) % mod;
x = x * x % mod;
ll y = query(t2, l, r) % mod;
cout << ((x - y) % mod + mod) % mod * q_pow(2, mod - 2) % mod << endl;
}
}
}
int main()
{
ios::sync_with_stdio(0);
int TT;cin>>TT;while(TT--)
work();
return 0;
}