1 最长上升子序列
1.1 题目描述
给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n≤5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
1.1.2 输入格式
第一行,一个整数 n n n,表示序列长度。
第二行有 n n n 个整数,表示这个序列。
1.1.3 输出格式
一个整数表示答案。
1.1.4 样例
样例输入
6
1 2 4 1 3 4
样例输出
4
1.2 题解
f i f_i fi:以编号 i i i 的元素结尾的最长上升子序列。
f i = m a x ( f j + 1 ) ( j < i 且 a j < a i ) f_i = max(f_j + 1) (j < i 且 a_j < a_i) fi=max(fj+1)(j<i且aj<ai)
考虑使用 树状数组 来保存 f i f_i fi 的值。
遍历,查找出 f 1 f_1 f1 到 f i − 1 f_{i-1} fi−1 之间的最大值,即为所求的 f j f_j fj.
最后找到 f 1 f_1 f1 到 f n f_n fn 之间的最大值,即为答案。
#include <bits/stdc++.h>
typedef long long ll;
const ll N = 1e6 + 10;
ll max_a, n, c[N], a[N];
inline ll lowbit(ll n) {return n & (-n);}
inline void updata(ll x, ll y) {
for (ll i = x;i <= max_a; i += lowbit(i))
c[i] = std::max(c[i], y);
}
inline ll find(ll x) {
ll res = 0;
for (ll i = x;i >= 1; i -= lowbit(i))
res = std::max(res, c[i]);
return res;
}
int main() {
scanf("%lld", &n);
for (ll i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
a[i]++;
max_a = std::max(a[i], max_a);
}
for (ll i = 1; i <= n; i++) {
ll k = find(a[i] - 1) + 1;
updata(a[i], k);
}
printf("%lld", find(max_a));
return 0;
}
2 区间改值和单值查询
2.1 题目描述
给定数组 a 1 , a 2 , . . . , a n ( ∣ a i ∣ ≤ 1 0 6 ) a_1,a_2,...,a_n(|a_i|\le 10^6) a1,a2,...,an(∣ai∣≤106),进行 q q q 次操作,操作有两种:
1 l r k
:将
a
l
a_l
al ~
a
r
(
1
≤
l
≤
r
≤
n
)
a_r(1\le l\le r\le n)
ar(1≤l≤r≤n) 全部加上
k
(
∣
k
∣
≤
1
0
6
)
k (|k|\le 10^6)
k(∣k∣≤106)
2 k
:输出
a
k
a_k
ak
2.1.2 输入格式
第一行:输入两个数 n n n, q ( 1 ≤ n , q ≤ 1 0 6 ) q(1\le n,q\le 10^6) q(1≤n,q≤106),表示给定数组的长度和操作数。
第二行:输入 n n n 个数,表示长度为 n n n 的给定数组。
接下来 q q q 行:每行输入表示如题的某一种操作。
2.1.3 输出格式
对于每个2操作,输出对应结果。
2.1.4 样例
样例输入
3 2
1 2 3
1 1 3 1
2 2
样例输出
3
2.2 题解
运用树状数组实现差分。
改值:将 [ l , r ] [l,r] [l,r] 区间的数都加 k k k ,等同于 [ l , n ] [l,n] [l,n] 区间加 k k k, [ r + 1 , n ] [r+1,n] [r+1,n] 区间减 k k k.
查询:差分数组的前缀和结果等于原数组,所以 a k = ∑ i = 1 k c k a_k=\sum_{i=1}^kc_k ak=∑i=1kck.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 10;
ll mina, n, q, c[N], a[N];
ll lowbit(ll n) {return n & (-n);}
void updata(ll l, ll r, ll x) {
for (ll i = l;i <= n; i += lowbit(i))
c[i] += x;
for (ll i = r + 1;i <= n; i += lowbit(i))
c[i] -= x;
}
ll count(ll x) {
ll res = 0;
for (ll i = x;i >= 1; i -= lowbit(i))
res += c[i];
return res;
}
int main() {
scanf("%lld%lld", &n, &q);
for (ll i = 1; i <= n; i++) {
cin >> a[i];
updata(i, i, a[i]);
}
while (q--) {
ll op, k, x, l, r;
scanf("%lld", &op);
if (op == 1) {
scanf("%lld%lld%lld", &l, &r, &x);
updata(l, r, x);
} else {
scanf("%lld", &k);
printf("%lld\n", count(k));
}
}
return 0;
}
3 区间改值和区间求和
3.1 题目描述
给定数组 a 1 , a 2 , . . . , a n ( ∣ a i ∣ ≤ 1 0 6 ) a_1,a_2,...,a_n(|a_i|\le 10^6) a1,a2,...,an(∣ai∣≤106),进行 q q q 次操作,操作有两种:
1 l r k
:将
a
l
a_l
al ~
a
r
(
1
≤
l
≤
r
≤
n
)
a_r(1\le l\le r\le n)
ar(1≤l≤r≤n) 全部加上
k
(
∣
k
∣
≤
1
0
6
)
k (|k|\le 10^6)
k(∣k∣≤106)
2 l r
:输出
∑
i
=
l
r
a
i
\sum_{i=l}^ra_i
∑i=lrai
3.1.2 输入格式
第一行:输入两个数 n n n, q ( 1 ≤ n , q ≤ 1 0 6 ) q(1\le n,q\le 10^6) q(1≤n,q≤106),表示给定数组的长度和操作数。
第二行:输入 n n n 个数,表示长度为 n n n 的给定数组。
接下来 q q q 行:每行输入表示如题的某一种操作。
3.1.3 输出格式
对于每个2操作,输出对应结果。
3.1.4 样例
样例输入
5 3
2 6 6 1 1
2 1 4
1 2 5 10
2 1 3
样例输出
15
34
3.2 题解
∑ i = 1 n a i = ∑ i = 1 n ∑ j = 1 i c i = ∑ i = 1 n ( n − i + 1 ) c i = ∑ i = 1 n ( n + 1 ) c i − ∑ i = 1 n i ⋅ x i \sum_{i=1}^{n}a_i=\sum_{i=1}^{n}\sum_{j=1}^ic_i =\sum_{i=1}^n(n-i+1)c_i =\sum_{i=1}^n(n+1)c_i-\sum_{i=1}^ni\cdot x_i i=1∑nai=i=1∑nj=1∑ici=i=1∑n(n−i+1)ci=i=1∑n(n+1)ci−i=1∑ni⋅xi
两个树状数组,一个差分(也就是计算
∑
i
=
1
n
(
n
+
1
)
c
i
\sum_{i=1}^n(n+1)c_i
∑i=1n(n+1)ci 这一部分),另一个计算
∑
i
=
1
n
i
⋅
x
i
\sum_{i=1}^ni\cdot x_i
∑i=1ni⋅xi 的值。
∑
i
=
l
r
a
i
=
∑
i
=
1
r
a
i
−
∑
i
=
1
l
−
1
a
i
\sum_{i=l}^ra_i=\sum_{i=1}^ra_i-\sum_{i=1}^{l-1}a_i
i=l∑rai=i=1∑rai−i=1∑l−1ai
根据这个公式,可求出操作2的值。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 10;
ll mina, n, q, c1[N], c2[N], a[N];
ll lowbit(ll n) {return n & (-n);}
void updata(ll l, ll r, ll x) {
for (ll i = l;i <= n; i += lowbit(i))
c1[i] += x, c2[i] += x * l;
for (ll i = r + 1;i <= n; i += lowbit(i))
c1[i] -= x, c2[i] -= x * (r + 1);
}
ll count(ll l, ll r) {
ll res = 0;
for (ll i = r;i >= 1; i -= lowbit(i))
res += c1[i] * (r + 1) - c2[i];
for (ll i = l - 1;i >= 1; i -= lowbit(i))
res -= c1[i] * l - c2[i];
return res;
}
int main() {
scanf("%lld%lld", &n, &q);
for (ll i = 1; i <= n; i++) {
cin >> a[i];
updata(i, i, a[i]);
}
while (q--) {
ll op, k, x, l, r;
scanf("%lld", &op);
if (op == 1) {
scanf("%lld%lld%lld", &l, &r, &x);
updata(l, r, x);
} else {
scanf("%lld%lld", &l, &r);
printf("%lld\n", count(l, r));
}
}
return 0;
}