算法笔记-线段树合并

news/2024/10/18 16:48:58/

线段树合并

前置知识:权值线段树、动态开点

将两棵线段树的信息合并成一棵线段树。
可以新建一颗线段树保存原来两颗线段树的信息,也可以将第二棵线段树维护的信息加到第一棵线段树上。

前者的空间复杂度较高,如果合并之前的线段树不会再用到的话,可以将第二颗线段树的信息加到第一棵线段树上。

P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

题意:

一棵树有 n n n 个点。每次操作 ( x , y , z ) (x,y,z) (x,y,z) 在路径 ( x , y ) (x,y) (x,y) 上的每一个点放一个救济粮 z z z。询问每个点存放最多的是哪种救济粮

解析:

对于树上一条路径上的点进行相同的操作,可以想到树上差分。

然后统计每个点最多的东西,可以用权值线段树维护每种救济粮的数目。

因为将发放救济粮转化成树上差分,求答案的时候需要合并,所以从下向上合并线段树。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;int head[maxn], tot;
struct edge{int to, nxt;
}e[maxn << 1];
struct node{int mcnt, id;int ls, rs;
}t[maxn * 60];
struct query{int x, y, z;
}q[maxn];int cnt;
int n, m, MAX;
int rt[maxn], ans[maxn];
void add(int a, int b){e[++tot].nxt = head[a];e[tot].to = b;head[a] = tot;
}
int dep[maxn], siz[maxn], top[maxn], son[maxn], fa[maxn];
void dfs1(int u, int p){dep[u] = dep[p] + 1;siz[u] = 1;fa[u] = p;for(int i = head[u]; i; i = e[i].nxt){int v = e[i].to;if(v == p)continue;dfs1(v, u);siz[u] += siz[v];if(siz[v] > siz[son[u]])son[u] = v;}
}
void dfs2(int u, int tp){top[u] = tp;if(son[u])dfs2(son[u], tp);for(int i = head[u]; i; i = e[i].nxt){int v = e[i].to;if(v == fa[u] || v == son[u])continue;dfs2(v, v);}
}
int LCA(int u, int v){while(top[u] != top[v]){if(dep[top[u]] < dep[top[v]])swap(u, v);u = fa[top[u]];}return dep[u] < dep[v] ? u : v;
}
void pushup(int k){if(t[t[k].ls].mcnt >= t[t[k].rs].mcnt){t[k].mcnt = t[t[k].ls].mcnt;t[k].id = t[t[k].ls].id;}else{t[k].mcnt = t[t[k].rs].mcnt;t[k].id = t[t[k].rs].id;}
}
void update(int &k, int l, int r, int pos, int v){if(k == 0)k = ++cnt;if(l == r && l == pos){t[k].mcnt += v;t[k].id = l;return;}int mid = (l+r) >> 1;if(pos <= mid)update(t[k].ls, l, mid, pos, v);elseupdate(t[k].rs, mid+1, r, pos, v);pushup(k);
}
void merge(int &a, int b, int l, int r){if(!a || !b){a = (!a ? b : a);return;}	if(l == r){t[a].mcnt += t[b].mcnt;t[a].id = l;return;}int mid = (l+r) >> 1;merge(t[a].ls, t[b].ls, l, mid);merge(t[a].rs, t[b].rs, mid+1, r);pushup(a);
}
void dfs(int u){for(int i = head[u]; i; i = e[i].nxt){int v = e[i].to;if(v == fa[u])continue;dfs(v);merge(rt[u], rt[v], 1, MAX);}if(t[rt[u]].mcnt != 0)ans[u] = t[rt[u]].id;
}
int main(){ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 1, u, v; i < n; i++){cin >> u >> v;add(u, v);add(v, u);}dfs1(1, 0);dfs2(1, 1);for(int i = 1; i <= m; i++){cin >> q[i].x >> q[i].y >> q[i].z;MAX = max(MAX, q[i].z);}for(int i = 1; i <= m; i++){int u = q[i].x, v = q[i].y;int w = q[i].z;int lca = LCA(u, v);update(rt[u], 1, MAX, w, 1);update(rt[v], 1, MAX, w, 1);update(rt[lca], 1, MAX, w, -1);if(fa[lca])update(rt[fa[lca]], 1, MAX, w, -1);}dfs(1);for(int i = 1; i <= n; i++)cout << ans[i] << endl;return 0;
}


P1600 [NOIP2016 提高组] 天天爱跑步

题意:

一棵 n n n 节点的树,有 m m m 条路径,每个节点有参数 w i w_i wi。询问有多少条路径的第 w i + 1 w_i+1 wi+1 个点是节点 i i i

解析:

对于每条路径 ( s , t ) (s, t) (s,t),可以分成两条路径 ( s , l c a ) (s,lca) (s,lca) ( l c a , t ) (lca,t) (lca,t),如果模拟这个过程的话,时间复杂度为 O ( n m ) O(nm) O(nm) 不能接受。

换个角度考虑,对于每个点,有多少条路径会对该点产生贡献。

设节点 i i i 的深度为 d e p i dep_i depi

设路径 ( s , t ) (s,t) (s,t) 对节点 u u u 产生贡献。

  • u u u ( s , l c a ) (s,lca) (s,lca) 上。 d e p s − d e p u = w u dep_s-dep_u = w_u depsdepu=wu
  • u u u ( l c a , t ) (lca, t) (lca,t) 上。 d e p s + d e p u − 2 d e p l c a = w u dep_s+dep_u-2dep_{lca} = w_u deps+depu2deplca=wu

即满足条件的路径会对节点 u u u 有贡献。

考虑树上差分,在 s s s 插入 d e p s dep_s deps,在 t t t 处插入 2 d e p l c a − d e p s 2dep_{lca}-dep_s 2deplcadeps,在 l c a lca lca 处插入 − d e p s -dep_s deps,在 f a ( l c a ) fa(lca) fa(lca) 处插入 d e p s − 2 d e p l c a dep_s-2dep_{lca} deps2deplca。后两者可以交换,然后线段树合并即可。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 3e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;int head[maxn], cnt;
struct edge{int to, nxt;
}e[maxn << 1];
struct node{int v;int ls, rs;
}t[maxn * 60];
int n, m;
int rt[maxn], MAX, ans[maxn], w[maxn], tot;
void add(int a, int b){e[++cnt].nxt = head[a];e[cnt].to = b;head[a] = cnt;
} 
int siz[maxn], son[maxn], dep[maxn], fa[maxn], top[maxn];
void dfs1(int u, int p){dep[u] = dep[p]+1;siz[u] = 1;fa[u] = p;for(int i = head[u]; i; i = e[i].nxt){int v = e[i].to;if(v == p)continue;dfs1(v, u);siz[u] += siz[v]; if(siz[v] > siz[son[u]])son[u] = v;}
}
void dfs2(int u, int tp){top[u] = tp;if(son[u])dfs2(son[u], tp);for(int i = head[u]; i; i = e[i].nxt){int v = e[i].to;if(v == son[u] || v == fa[u])continue;dfs2(v, v);}
}
int LCA(int u, int v){while(top[u] != top[v]){if(dep[top[u]] < dep[top[v]])swap(u, v);u = fa[top[u]];}return dep[u] < dep[v] ? u : v;
}
void update(int &k, int l, int r, int pos, int v){if(k == 0)k = ++tot;if(l == r && l == pos){t[k].v += v;return;}int mid = (l+r) >> 1;if(pos <= mid)update(t[k].ls, l, mid, pos, v);elseupdate(t[k].rs, mid+1, r, pos, v);return;
}
void merge(int &a, int b, int l, int r){if(!a || !b){a = (!a ? b : a);return;}if(l == r){t[a].v += t[b].v;return;}int mid = (l+r) >> 1;merge(t[a].ls, t[b].ls, l, mid);merge(t[a].rs, t[b].rs, mid+1, r);
}
int query(int k, int l, int r, int pos){if(!k)return 0;if(l == r)return t[k].v;int mid = (l+r) >> 1;if(pos <= mid)return query(t[k].ls, l, mid, pos);elsereturn query(t[k].rs, mid+1, r, pos);
}
void dfs(int u){for(int i = head[u]; i; i = e[i].nxt){int v = e[i].to;if(v == fa[u])continue;dfs(v);merge(rt[u], rt[v], 1, MAX);}if(w[u] && n + dep[u] + w[u] <= MAX)ans[u] += query(rt[u], 1, MAX, n + dep[u] + w[u]);ans[u] += query(rt[u], 1, MAX, n + dep[u] - w[u]);}int main(){ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> n >> m;MAX = n << 1;int u, v;for(int i = 1; i < n; i++){cin >> u >> v;add(u, v);add(v, u);}dfs1(1, 0);dfs2(1, 1);for(int i = 1; i <= n; i++)cin >> w[i];for(int i = 1; i <= m; i++){cin >> u >> v;int lca = LCA(u, v);update(rt[u], 1, MAX, n + dep[u], 1);update(rt[v], 1, MAX, n + dep[lca] * 2 - dep[u], 1);update(rt[lca], 1, MAX, n + dep[u], -1);update(rt[fa[lca]], 1, MAX, n + dep[lca] * 2 - dep[u], -1);}dfs(1);for(int i = 1; i <= n; i++)cout << ans[i] << " ";cout << endl;return 0;
}


P3224 [HNOI2012]永无乡

题意:

n n n 个节点,每个节点有互不相同的重要程度。两种操作:

  • ( x , y ) (x,y) (x,y) 之间建桥
  • 询问与节点 x x x 所在连通块中重要程度排名第 k k k 小的节点编号

解析:

查询第 k k k 小,考虑权值线段树;维护联通性,考虑并查集。在合并两个连通块时,也合并权值线段树

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 3e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;struct node{int sum, id;int ls, rs;
}t[maxn * 60];
int rt[maxn];
int tot, n, m, q;
int fa[maxn];
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int pushup(int k){t[k].sum = t[t[k].ls].sum + t[t[k].rs].sum;
}
void update(int &k, int l, int r, int pos, int idx){if(k == 0)k = ++tot;if(l == r){t[k].sum++;t[k].id = idx;return;}int mid = (l+r) >> 1;if(pos <= mid)update(t[k].ls, l, mid, pos, idx);elseupdate(t[k].rs, mid+1, r, pos, idx);pushup(k);
}
void merge(int &a, int b, int l, int r){if(!a || !b){a = (a == 0 ? b : a);return;}if(l == r){t[a].sum += t[b].sum;return;}int mid = (l+r) >> 1;merge(t[a].ls, t[b].ls, l, mid);merge(t[a].rs, t[b].rs, mid+1, r);pushup(a);
}
int query(int a, int k, int l, int r){if(t[a].sum < k || !a)return 0;if(l == r)return t[a].id;int mid = (l+r) >> 1;int ans = 0;if(k <= t[t[a].ls].sum)ans = query(t[a].ls, k, l, mid);elseans = query(t[a].rs, k-t[t[a].ls].sum, mid+1, r);return ans;
}
int main(){ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 1, p; i <= n; i++){fa[i] = i;cin >> p;update(rt[i], 1, n, p, i);}for(int i = 1, x, y; i <= m; i++){cin >> x >> y;int fx = find(x);int fy = find(y);fa[fy] = fx;merge(rt[fx], rt[fy], 1, n);}cin >> q;string op;for(int i = 1, x, y; i <= q; i++){cin >> op >> x >> y;if(op == "B"){int fx = find(x);int fy = find(y);if(fx == fy)continue;fa[fy] = fx;merge(rt[fx], rt[fy], 1, n); }else if(op == "Q"){int fx = find(x);int res = query(rt[fx], y, 1, n);if(res == 0)cout << -1 << endl;elsecout << res << endl;}}	return 0;
}

http://www.ppmy.cn/news/212257.html

相关文章

寒假玩游戏哪款蓝牙耳机好用?佩戴舒适,主动降噪,这五款真绝了~

进入冬季以来&#xff0c;温度也日益下降&#xff0c;特别是每日的早晚时分&#xff0c;很多地区动辄接近零度&#xff0c;着实影响我们的日常活动。正好也要放寒假了&#xff0c;疫情没结束&#xff0c;还是不要到处晃荡了&#xff0c;宅在家玩玩游戏打发时间或许是不错的选择…

Pulseaudio之nemo(二十二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. ​

苹果正制造一款疯狂的“16K”VR耳机,效果到底有多牛?

苹果一直以来都有传闻称正在研发一副增强现实眼镜&#xff0c;但今天的一份报告显示&#xff0c;他们希望在虚拟现实领域与Google&#xff0c;微软和Facebook竞争。 据CNET报道&#xff0c;苹果公司着眼于2020年发布的将AR和VR技术相结合的无线耳机。那么该VR耳机效果到底牛到什…

再次解决,android 2.3运行凯立德问题

我的Hero最近刷了2.3的ROM,原来在2.1下可以使用的凯立德又FC了&#xff0c;估计又是android的API接口改变了&#xff0c;又不兼容了&#xff0c;还好当时在有过解决在1.5时代到2.1凯立德不兼容的经验&#xff08;这个当时也我第一个发布可以在2.1下使用的凯立德&#xff0c;htt…

相同型号设备(手机、耳机)同时插入电脑识别不同设备号问题

一 背景 相同型号耳机或手机等设备插入电脑识别成播放或录音设备&#xff1b;更换相同型号产品时&#xff0c;产品会重新枚举&#xff0c;播放设备号不唯一。如图 如果插入多个相同型号头戴式耳机&#xff0c;设备名称枚举时会多带出一个数字“2”或者其他。 二 解决方法 1 修…

cortex m3 开源_开源增强现实耳机,Steam的125M有效帐户等

cortex m3 开源 您好&#xff0c;开放游戏迷&#xff01; 在本周的版本中&#xff0c;我们将了解Steam的1.25亿活跃帐户和Game Developers Conference&#xff0c;这是一个开源增强现实头戴设备&#xff0c;Linux游戏等。 开放游戏综述 2015年2月21日至28日当周 Steam拥有12…

测评 | 谷歌智能耳机Pixel Buds体验:耳朵里的语音助手

本文系网易智能工作室出品&#xff0c; 聚焦AI&#xff0c;读懂下一个大时代&#xff01; 大型年度AI人物评选——2017中国AI英雄风云榜&#xff0c;自荐提名进行中&#xff01; 奖项设置&#xff1a;技术创新人物TOP 10&#xff0c;商业创新人物TOP 10 表彰人物&#xff1a;华…

旧改快讯--星河操刀,龙华稳健工业园项目专规获批

龙华街道稳健工业园城市更新单元原列入《2019年深圳市龙华区城市更新单元计划第五批计划》&#xff0c;现已列入《2022年深圳市龙华区城市更新单元计划第三批计划》&#xff0c;现该更新单元规划已经深圳市城市规划委员会法定图则委员会2023年第16次会议审议并获原则通过&#…