线段树的基本操作与线段树合并
普通线段树#
P3372 【模板1】线段树1#
使用线段树进行维护。
#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 100010;
struct SegmentTree {
int l, r;
int val;
int tag;
}tr[N << 2];
int a[N];
void pushup(int u) {
tr[u].val = tr[u << 1].val + tr[u << 1 | 1].val;
}
void addtag(int u, int v) {
tr[u].tag += v;
tr[u].val += v * (tr[u].r - tr[u].l + 1);
}
void pushdown(int u) {
if (tr[u].tag) {
addtag(u << 1, tr[u].tag);
addtag(u << 1 | 1, tr[u].tag);
tr[u].tag = 0;
}
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].val = a[l];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].val;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1, sum = 0;
if (l <= mid) sum += query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
void modify(int u, int l, int r, int v) {
if (l <= tr[u].l && tr[u].r <= r) {
addtag(u, v);
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, v);
if (r > mid) modify(u << 1 | 1, l, r, v);
pushup(u);
}
int n, m;
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
int a, b, c, d;
for (int i = 1; i <= m; i++) {
cin >> a;
if (a == 1) {
cin >> b >> c >> d;
modify(1, b, c, d);
}
else {
cin >> b >> c;
cout << query(1, b, c) << '\n';
}
}
return 0;
}
P4588 [TJOI2018]数学计算#
以操作为元素建立一棵线段树,维护乘积(要
对于第
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
struct SegmentTree {
int l, r;
int val;
}tr[N << 2];
int n, m;
void pushup(int u) {
tr[u].val = (1ll * tr[u << 1].val * tr[u << 1 | 1].val)% m;
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r, tr[u].val = 1;
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
void modify(int u, int x, int v) {
if (tr[u].l == tr[u].r) {
tr[u].val = v;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
void solve() {
cin >> n >> m;
build(1, 1, n);
int opt, x;
for (int i = 1; i <= n; i++) {
cin >> opt >> x;
if (opt == 1) modify(1, i, x);
else modify(1, x, 1);
cout << tr[1].val << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) solve();
return 0;
}
P1471 方差#
首先可以想到这是使用线段树的。
我们对方差公式
所以我们要维护一个
#include <iostream>
#include <cstring>
#include <algorithm>
#include <iomanip>
using namespace std;
const int N = 100010;
struct SegmentTree {
double sum; // Σai
double get; // Σai²
double tag;
}tr[N * 4];
int n, m;
double a[N];
void tag(int u, int l, int r, double v) {
tr[u].tag += v;
tr[u].get += v * v * (r - l + 1) + 2 * v * tr[u].sum;
tr[u].sum += v * (r - l + 1);
}
void pushdown(int u, int l, int r) {
if (tr[u].tag) {
int mid = l + r >> 1;
tag(u << 1, l, mid, tr[u].tag);
tag(u << 1 | 1, mid + 1, r, tr[u].tag);
tr[u].tag = 0;
}
}
void pushup(SegmentTree& ans, SegmentTree l, SegmentTree r) {
ans.sum = l.sum + r.sum;
ans.get = l.get + r.get;
}
void pushup(int u) {
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void build(int u, int l, int r) {
if (l == r) {
tr[u].sum = a[l];
tr[u].get = a[l] * a[l];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int pl, int pr, double v) {
if (pl <= l && r <= pr) {
tag(u, l, r, v);
return;
}
pushdown(u, l, r);
int mid = l + r >> 1;
if (pl <= mid) modify(u << 1, l, mid, pl, pr, v);
if (pr > mid) modify(u << 1 | 1, mid + 1, r, pl, pr, v);
pushup(u);
}
SegmentTree query(int u, int l, int r, int pl, int pr) {
if (pl <= l && r <= pr) {
return tr[u];
}
pushdown(u, l, r);
int mid = l + r >> 1;
if (pr <= mid) return query(u << 1, l, mid, pl, pr);
else if (pl > mid) return query(u << 1 | 1, mid + 1, r, pl, pr);
else {
SegmentTree lans, rans, ans;
lans = query(u << 1, l, mid, pl, pr);
rans = query(u << 1 | 1, mid + 1, r, pl, pr);
pushup(ans, lans, rans);
return ans;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(4);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
int opt, a, b;
double c;
while (m--) {
cin >> opt;
if (opt == 1) {
cin >> a >> b >> c;
modify(1, 1, n, a, b, c);
}
else if (opt == 2) {
cin >> a >> b;
SegmentTree ans = query(1, 1, n, a, b);
double avg = ans.sum / (b - a + 1);
cout << avg << '\n';
}
else {
cin >> a >> b;
SegmentTree ans = query(1, 1, n, a, b);
int len = b - a + 1;
double avg = ans.sum / len;
double res = 1.0 / len * (ans.get + len * avg * avg - 2 * avg * ans.sum);
cout << res << '\n';
}
}
return 0;
}
动态开点线段树#
即在需要时才开点。
P3372 【模板1】线段树1#
强制使用动态开点线段树来做这一题。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
using i64 = long long;
const int N = 100010;
struct SegmentTree {
int l, r;
i64 sum, tag;
}tr[N << 1];
int tot = 1, root = 1;
int build() {
tot++, tr[tot].l = tr[tot].r = tr[tot].sum = tr[tot].tag = 0;
return tot;
}
void tag(int& cur, int l, int r, i64 v) {
if (!cur) cur = build();
tr[cur].tag += v;
tr[cur].sum += v * (r - l + 1);
}
void pushup(int cur) {
tr[cur].sum = tr[tr[cur].l].sum + tr[tr[cur].r].sum;
}
void pushdown(int cur, int l, int r) {
if (l >= r || !tr[cur].tag) return;
int mid = (l + r - 1) / 2;
tag(tr[cur].l, l, mid, tr[cur].tag);
tag(tr[cur].r, mid + 1, r, tr[cur].tag);
tr[cur].tag = 0;
}
void add(int& cur, int l, int r, int _left, int _right, i64 val) {
if (!cur) cur = build();
if (_left <= l && r <= _right) {
tag(cur, l, r, val);
return;
}
pushdown(cur, l, r);
int mid = (l + r - 1) / 2;
if (_left <= mid) add(tr[cur].l, l, mid, _left, _right, val);
if (_right > mid) add(tr[cur].r , mid + 1, r, _left, _right, val);
pushup(cur);
}
i64 query(int& cur, int l, int r, int _left, int _right) {
if (!cur) return 0;
if (_left <= l && r <= _right) {
return tr[cur].sum;
}
pushdown(cur, l, r);
int mid = (l + r - 1) / 2;
i64 sum = 0;
if (_left <= mid) sum += query(tr[cur].l, l, mid, _left, _right);
if (_right > mid) sum += query(tr[cur].r , mid + 1, r, _left, _right);
return sum;
}
int n, m;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
add(root, 1, n, i, i, x);
}
int op, x, y;
i64 z;
while (m--) {
cin >> op;
if (op == 1) {
cin >> x >> y >> z;
add(root, 1, n, x, y, z);
}
else {
cin >> x >> y;
cout << query(root, 1, n, x, y) << '\n';
}
}
return 0;
}
权值线段树#
线段树维护的区间是一个值域范围,而不是一段下标的区间。
P1637 三元上升子序列#
我们可以考虑中间的数字
使用权值线段树,边处理边询问,从前往后枚举
统计比
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
using i64 = long long;
const int N = 100010;
struct SegmentTree {
int l, r;
int val;
}tr[N << 2];
int a[N], n;
void pushup(int u) {
tr[u].val = tr[u << 1].val + tr[u << 1 | 1].val;
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].val = 0;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int x, int v) {
if (tr[u].l == x && x == tr[u].r) {
tr[u].val += v;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].val;
}
int mid = tr[u].l + tr[u].r >> 1, sum = 0;
if (l <= mid) sum += query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
int small[N], big[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, N - 1);
for (int i = 1; i <= n; i++) {
small[i] = query(1, 1, a[i] - 1);
modify(1, a[i], 1);
}
build (1, 1, N - 1);
for (int i = n; i >= 1; i--) {
big[i] = query(1, a[i] + 1, N - 1);
modify(1, a[i], 1);
}
i64 ans = 0;
for (int i = 1; i <= n; i++) ans += 1ll * small[i] * big[i];
cout << ans << '\n';
return 0;
}
P1908 逆序对#
与上一题类似,但是需要离散化。
离散化:是指将大的值域范围对应到小的值域范围,且不改变元素之间的相对大小。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <unordered_map>
using namespace std;
using i64 = long long;
const int N = 1000010;
struct SegmentTree {
int l, r;
int val;
}tr[N << 2];
int a[N], b[N], tot, n;
void pushup(int u) {
tr[u].val = tr[u << 1].val + tr[u << 1 | 1].val;
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].val = 0;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int x, int v) {
if (tr[u].l == x && x == tr[u].r) {
tr[u].val += v;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].val;
}
int mid = tr[u].l + tr[u].r >> 1, sum = 0;
if (l <= mid) sum += query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
int small[N], big[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
memcpy(b, a, sizeof(a));
sort(b + 1, b + n + 1);
tot = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b;
build(1, 1, tot);
for (int i = 1; i <= n; i++) {
small[i] = query(1, a[i] + 1, tot);
modify(1, a[i], 1);
}
i64 ans = 0;
for (int i = 1; i <= n; i++) ans += small[i];
cout << ans << '\n';
return 0;
}
P5094 [USACO04OPEN] MooFest G#
暴力:
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
ans += 1ll * abs(x[i] - x[j]) * max(v[i], v[j]);
}
}
优化策略:
-
按照
值将牛从小到大排序; -
枚举牛
,计算牛 , 与前 头牛产生的音量之和。
可以建立两棵线段树,来快速得出
第一棵:以坐标为区间,以牛数为元素,维护
第二棵:以坐标为区间,以牛的坐标为元素,维护
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
using i64 = long long;
using PIL = pair<int, i64>;
const int N = 50010;
int n;
struct cow {
int x, v;
}a[N];
struct SegmentTree {
int l, r;
int cnt;
i64 sum;
}tr[N << 2];
void pushup(int u) {
tr[u].cnt = tr[u << 1].cnt + tr[u << 1 | 1].cnt;
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r) {
tr[u].l = l;
tr[u].r = r;
if (l == r) return;
int mid = (l + r - 1) / 2;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
PIL query(int u, int l, int r) {
if (l > r) return { 0, 0 };
if (l <= tr[u].l && tr[u].r <= r) {
return { tr[u].cnt, tr[u].sum };
}
int mid = (tr[u].l + tr[u].r - 1) / 2;
PIL ans = { 0, 0 };
if (l <= mid) {
PIL get = query(u << 1, l, r);
ans.first += get.first;
ans.second += get.second;
}
if (r > mid) {
PIL get = query(u << 1 | 1, l, r);
ans.first += get.first;
ans.second += get.second;
}
return ans;
}
void modify(int u, int x) {
if (tr[u].l == tr[u].r) {
tr[u].cnt++;
tr[u].sum += x;
return;
}
int mid = (tr[u].l + tr[u].r - 1) / 2;
if (x <= mid) modify(u << 1, x);
else modify(u << 1 | 1, x);
pushup(u);
}
i64 ans, possum;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
build(1, 1, N - 1);
for (int i = 1; i <= n; i++) cin >> a[i].v >> a[i].x;
sort(a + 1, a + n + 1, [](const cow& a, const cow& b) { return a.v < b.v; });
for (int i = 1; i <= n; i++) {
PIL q1 = query(1, 1, a[i].x - 1);
PIL q2 = { i - q1.first - 1, possum - q1.second};
i64 cnt1 = q1.first, sum1 = q1.second;
i64 cnt2 = q2.first, sum2 = q2.second;
ans += a[i].v * (cnt1 * a[i].x - sum1 - cnt2 * a[i].x + sum2);
modify(1, a[i].x);
possum += a[i].x;
}
cout << ans << '\n';
return 0;
}
线段树合并#
CF600E Lomsat gelral#
在每个节点上建立一棵权值线段树,然后每个节点还会记录其子树(指线段树)中颜色出现最多的数量以及编号之和。
最后自下而上将线段树合并即可,即将子树信息合并到父结点上(指题目中的树),合并时需要将次数累加。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
using i64 = long long;
const int N = 100010;
struct SegmentTree {
int l, r;
i64 val;
i64 max;
}tr[N * 70];
int root[N], tot;
int n;
void pushup(int cur) {
if (tr[tr[cur].l].val < tr[tr[cur].r].val) tr[cur].val = tr[tr[cur].r].val, tr[cur].max = tr[tr[cur].r].max;
else if (tr[tr[cur].l].val > tr[tr[cur].r].val) tr[cur].val = tr[tr[cur].l].val, tr[cur].max = tr[tr[cur].l].max;
else tr[cur].val = tr[tr[cur].l].val, tr[cur].max = tr[tr[cur].l].max + tr[tr[cur].r].max;
}
void modify(int& cur, int _left, int _right, int x, i64 v) {
if (!cur) cur = ++tot;
if (_left == x && _right == x) {
tr[cur].val += v;
tr[cur].max = x;
return;
}
int mid = (_left + _right - 1) / 2;
if (x <= mid) modify(tr[cur].l, _left, mid, x, v);
else modify(tr[cur].r, mid + 1, _right, x, v);
pushup(cur);
}
struct Edge {
int to, next;
}e[N * 2];
int head[N], idx;
void add(int a, int b) {
idx++, e[idx].to = b, e[idx].next = head[a], head[a] = idx;
idx++, e[idx].to = a, e[idx].next = head[b], head[b] = idx;
}
int merge(int cur1, int cur2, int _left, int _right) {
if ((!cur1) || (!cur2)) return cur1 | cur2;
if (_left == _right) {
tr[cur1].val += tr[cur2].val;
tr[cur1].max = _left;
return cur1;
}
int mid = (_left + _right - 1) / 2;
tr[cur1].l = merge(tr[cur1].l, tr[cur2].l, _left, mid);
tr[cur1].r = merge(tr[cur1].r, tr[cur2].r, mid + 1, _right);
pushup(cur1);
return cur1;
}
i64 ans[N];
void dfs(int u, int fa) {
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs(to, u);
root[u] = merge(root[u], root[to], 1, N - 1);
}
if (tr[root[u]].val != 0) {
ans[u] = tr[root[u]].max;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
modify(root[i], 1, N - 1, x, 1);
}
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
add(x, y);
}
dfs(1, 0);
for (int i = 1; i <= n; i++) cout << ans[i] << ' ';
return 0;
}
CF246E Blood Cousins Return#
- 对询问进行离线处理。
- 对于每个树上节点
,以深度为元素建立一个线段树,元素数据类型为set<string>
。当进行合并时,可以把相同深度的set
进行合并。 - 当一个查询为
时,输出 节点上线段树的第 个元素的set
的大小size
即可。 - 同时本题只需要访问线段树的叶子节点,所以不需要
pushup
操作(否则会MLE
)。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <unordered_map>
using namespace std;
using PII = pair<int, int>;
const int N = 100010;
struct Edge {
int to, next;
}e[N * 2];
int head[N], idx;
void add(int a, int b) {
idx++, e[idx].to = b, e[idx].next = head[a], head[a] = idx;
}
struct SegmentTree {
int l, r;
set<string> s;
}tr[N * 20];
int root[N], tot;
void mergeset(set<string>& a, set<string>& b) { // a<=b
for (auto& x : b) a.insert(x);
}
void modify(int& cur, int l, int r, int x, string s) {
if (!cur) cur = ++tot;
if (l == r) {
tr[cur].s.insert(s);
return;
}
int mid = (l + r - 1) / 2;
if (x <= mid) modify(tr[cur].l, l, mid, x, s);
else modify(tr[cur].r, mid + 1, r, x, s);
}
int n, m;
vector<PII> query[N];
int ans[N];
int merge(int cur1, int cur2, int l, int r) {
if ((!cur1) || (!cur2)) return cur1 | cur2;
if (l == r) {
mergeset(tr[cur1].s, tr[cur2].s);
return cur1;
}
int mid = (l + r - 1) / 2;
tr[cur1].l = merge(tr[cur1].l, tr[cur2].l, l, mid);
tr[cur1].r = merge(tr[cur1].r, tr[cur2].r, mid + 1, r);
return cur1;
}
int ask(int cur, int l, int r, int x) {
if (!cur) return 0;
if (l == r) {
return tr[cur].s.size();
}
int mid = (l + r - 1) / 2;
if (x <= mid) return ask(tr[cur].l, l, mid, x);
else return ask(tr[cur].r, mid + 1, r, x);
}
int dep[N];
string s[N];
void dfs1(int u, int fa) {
dep[u] = dep[fa] + 1;
modify(root[u], 1, N - 1, dep[u], s[u]);
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs1(to, u);
}
}
void dfs2(int u, int fa) {
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs2(to, u);
root[u] = merge(root[u], root[to], 1, N - 1);
}
for (int i = 0; i < query[u].size(); i++) {
int d = query[u][i].first, id = query[u][i].second;
ans[id] = ask(root[u], 1, N - 1, dep[u] + d);
}
}
vector<int> treeroot;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
int fa;
cin >> s[i] >> fa;
if (fa == 0) {
treeroot.push_back(i);
continue;
}
add(fa, i);
add(i, fa);
}
for (auto& x : treeroot) dfs1(x, 0);
cin >> m;
for (int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
query[x].push_back({y, i});
}
for (auto& x : treeroot) dfs2(x, 0);
for (int i = 1; i <= m; i++) cout << ans[i] << '\n';
return 0;
}
CF208E Blood Cousins#
跟CF246E的题目名称长得很像
通过倍增,求出
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
using PII = pair<int, int>;
const int N = 100010;
struct Edge {
int to, next;
}e[N * 2];
int head[N], idx;
void add(int a, int b) {
idx++, e[idx].to = b, e[idx].next = head[a], head[a] = idx;
}
struct SegmentTree {
int l, r;
int s;
}tr[N * 20];
int root[N], tot;
void modify(int& cur, int l, int r, int x, int v) {
if (!cur) cur = ++tot;
if (l == r) {
tr[cur].s += v;
return;
}
int mid = (l + r - 1) / 2;
if (x <= mid) modify(tr[cur].l, l, mid, x, v);
else modify(tr[cur].r, mid + 1, r, x, v);
}
int n, m;
vector<PII> query[N];
int ans[N];
int merge(int cur1, int cur2, int l, int r) {
if ((!cur1) || (!cur2)) return cur1 | cur2;
if (l == r) {
tr[cur1].s += tr[cur2].s;
return cur1;
}
int mid = (l + r - 1) / 2;
tr[cur1].l = merge(tr[cur1].l, tr[cur2].l, l, mid);
tr[cur1].r = merge(tr[cur1].r, tr[cur2].r, mid + 1, r);
return cur1;
}
int ask(int cur, int l, int r, int x) {
if (!cur) return 0;
if (l == r) {
return tr[cur].s;
}
int mid = (l + r - 1) / 2;
if (x <= mid) return ask(tr[cur].l, l, mid, x);
else return ask(tr[cur].r, mid + 1, r, x);
}
int dep[N];
int f[N][20];
void dfs1(int u, int fa) {
dep[u] = dep[fa] + 1;
f[u][0] = fa;
modify(root[u], 1, N - 1, dep[u], 1);
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs1(to, u);
}
}
void initf() {
for (int j = 1; j < 20; j++) {
for (int i = 1; i <= n; i++) {
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
}
void dfs2(int u, int fa) {
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs2(to, u);
root[u] = merge(root[u], root[to], 1, N - 1);
}
for (int i = 0; i < query[u].size(); i++) {
int d = query[u][i].first, id = query[u][i].second;
ans[id] = ask(root[u], 1, N - 1, dep[u] + d);
}
}
int getfa(int cur, int step_fa) {
for (int i = 19; i >= 0; i--) {
if (step_fa >> i & 1) {
cur = f[cur][i];
}
}
return cur;
}
vector<int> treeroot;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
int fa;
cin >> fa;
if (fa == 0) {
treeroot.push_back(i);
continue;
}
add(fa, i);
add(i, fa);
}
for (auto& x : treeroot) dfs1(x, 0);
initf();
cin >> m;
for (int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
int g = getfa(x, y);
query[g].push_back({y, i});
}
for (auto& x : treeroot) dfs2(x, 0);
for (int i = 1; i <= m; i++) {
if (ans[i]) cout << ans[i] - 1;
else cout << 0;
cout << ' ';
}
return 0;
}
CF490F Treeland Tour#
来自洛谷 CF490F的翻译:给出一棵带点权树,求树上最长上升子序列的长度。
考虑先计算出子树的所有信息,然后再合并到父亲节点。
设
设
情况1:对于以
为根节点的子树,经过 的 LIS 的长度。 处理方法:
暴力:暴力枚举树上的每一个点进行转移。
每个树上节点以
为值域,以 为元素维护一棵线段树。 最后找一个比
小的 且 是最大的元素进行转移,即 。 同时,当线段树合并时,也可以进行合并:
是为了处理
的情况。
情况2:对于以
为根节点的子树,不经过 的 LIS 长度为所有子节点的 LIS 取最值。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 6010, V = 1000000;
struct Edge {
int to, next;
}e[N * 2];
int head[N], idx;
void add(int a, int b) {
idx++;
e[idx].to = b;
e[idx].next = head[a];
head[a] = idx;
}
int n;
int a[N], ans;
struct SegmentTree {
int l, r;
int lis;
int lds;
}tr[N * 25];
int root[N], tot;
void pushup(int u) {
tr[u].lis = max(tr[tr[u].l].lis, tr[tr[u].r].lis);
tr[u].lds = max(tr[tr[u].l].lds, tr[tr[u].r].lds);
}
void modify(int& u, int l, int r, int x, int v1, int v2) {
if (!u) u = ++tot;
if (l == r) {
tr[u].lis = max(tr[u].lis, v1);
tr[u].lds = max(tr[u].lds, v2);
return;
}
int mid = l + r >> 1;
if (x <= mid) modify(tr[u].l, l, mid, x, v1, v2);
else modify(tr[u].r, mid + 1, r, x, v1, v2);
pushup(u);
}
int query(int u, int l, int r, int _left, int _right, int type) {
if (!u) return 0;
if (_left <= l && r <= _right) {
if (type == 1) return tr[u].lis;
else return tr[u].lds;
}
int res = 0;
int mid = l + r >> 1;
if (_left <= mid) res = max(res, query(tr[u].l, l, mid, _left, _right, type));
if (_right > mid) res = max(res, query(tr[u].r, mid + 1, r, _left, _right, type));
return res;
}
int merge(int cur1, int cur2, int l, int r) {
if ((!cur1) || (!cur2)) return cur1 | cur2;
int mid = l + r >> 1;
tr[cur1].lis = max(tr[cur1].lis, tr[cur2].lis);
tr[cur1].lds = max(tr[cur1].lds, tr[cur2].lds);
ans = max(ans, max(tr[tr[cur1].l].lis + tr[tr[cur2].r].lds, tr[tr[cur1].r].lds + tr[tr[cur2].l].lis));
tr[cur1].l = merge(tr[cur1].l, tr[cur2].l, l, mid);
tr[cur1].r = merge(tr[cur1].r, tr[cur2].r, mid + 1, r);
return cur1;
}
void dfs(int u, int fa) {
int maxf = 0, maxxf = 0;
int maxg = 0, maxxg = 0;
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs(to, u);
int x = query(root[to], 1, V, 1, a[u] - 1, 1);
int y = query(root[to], 1, V, a[u] + 1, V, 2);
ans = max(ans, maxf + y + 1);
ans = max(ans, x + maxg + 1);
maxf = max(maxf, x);
maxg = max(maxg, y);
root[u] = merge(root[u], root[to], 1, V);
}
modify(root[u], 1, V, a[u], maxf + 1, maxg + 1);
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
modify(root[i], 1, V, a[i], 1, 1);
}
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
add(x, y);
add(y, x);
}
dfs(1, 0);
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具