板子 lca

这篇博客介绍了三种不同的算法来查找树中两点的最近公共祖先:Tarjan离线算法,倍增算法和RMQ(Range Minimum Query)结合ST表的方法。每种算法的时间复杂度分别为O(nlogn+q),O(nlogn)和O(n*log2(n)),查询时间为O(1)。这些方法都是针对离线查询的优化,并通过具体代码实现来说明了如何在实际问题中应用这些算法。

模板题

tarjan做法:

离线

总时间复杂度是O(nlogn+q)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=500010;
struct ty{
	ll t,next;
}edge[N<<1];
ll cnt1=0;
ll head[N];
ll n,m,s;
ll a,b;
template<typename Type>inline void read(Type &xx)
{
    Type f=1;char ch;xx=0;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())xx=xx*10+ch-'0';
    xx*=f;
}
struct ty2//询问结构体 
{
	ll t,next;
	ll same;//会有一组与其相同的询问,
	//每次遍历到一组时要同时把另一组去掉
	ll num;//询问的编号,最后要按顺序输出
	bool flag=0;//标记该询问是否已经被回答过	 
}q[N<<1];
void addedge(ll a,ll b)
{
	edge[++cnt1].t=b;
	edge[cnt1].next=head[a];
	head[a]=cnt1;
}
ll cnt2=0;
ll head2[N];
ll fa[N];//并查集
void addque(ll a,ll b,ll c/*记录序号*/)
{
	q[++cnt2].t=b;
	q[cnt2].next=head2[a];
	head2[a]=cnt2;
	q[cn
提供的引用内容中未涉及C++虚树的板子题相关信息。在常见的算法竞赛平台上能找到C++虚树的板子题。以下是一些推荐: - **洛谷P2495 [SDOI2011]消耗战**:题目要求在一棵树上,给出若干询问,每次询问指定一些关键点,要切断从根节点到所有关键点的路径,求最小的边权和。此问题适合用虚树来解决,通过构建虚树,能将每次询问的时间复杂度降低。 - **Codeforces 613D Kingdom and its Cities**:该题给出一棵树,多次询问一些点的集合,要判断能否通过删除一些点,使得集合内任意两点不连通,若可以,求最少删除点数。借助虚树可以高效处理每次询问。 ```cpp // 以下是一个简单的虚树构建的伪代码示例 #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAXN = 100005; vector<int> G[MAXN]; int dep[MAXN], fa[MAXN], top[MAXN], son[MAXN], sz[MAXN]; // 树链剖分相关数组 // 树链剖分初始化 void dfs1(int u, int f) { fa[u] = f; dep[u] = dep[f] + 1; sz[u] = 1; for (int v : G[u]) { if (v == f) continue; dfs1(v, u); sz[u] += sz[v]; if (sz[v] > sz[son[u]]) son[u] = v; } } void dfs2(int u, int t) { top[u] = t; if (son[u]) dfs2(son[u], t); for (int v : G[u]) { if (v == fa[u] || v == son[u]) continue; dfs2(v, v); } } // 求LCA 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; } // 构建虚树 vector<int> vt[MAXN]; bool cmp(int u, int v) { return dep[u] < dep[v]; } void build_virtual_tree(vector<int> &key_points) { sort(key_points.begin(), key_points.end(), cmp); vector<int> stk; stk.push_back(1); // 根节点 for (int u : key_points) { if (u == 1) continue; int l = lca(u, stk.back()); while (1) { if (dep[l] >= dep[stk[stk.size() - 2]]) { if (l != stk.back()) { vt[l].push_back(stk.back()); if (l != stk[stk.size() - 2]) stk.push_back(l); } break; } vt[stk[stk.size() - 2]].push_back(stk.back()); stk.pop_back(); } stk.push_back(u); } while (stk.size() > 1) { vt[stk[stk.size() - 2]].push_back(stk.back()); stk.pop_back(); } } int main() { // 这里可以添加读取树的边和关键点的代码 vector<int> key_points = {2, 3, 4}; // 示例关键点 build_virtual_tree(key_points); return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值