lca 模板

 倍增lca

O(mlogn)

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'

using namespace std;

typedef pair<int, int> PII;
typedef long long ll;

const int N = 40010, M = 16;

int n, m;
vector<int> g[N];
int root;
int depth[N];
int f[N][M];

void bfs()
{
	memset(depth, 0x3f, sizeof depth);
	depth[0] = 0, depth[root] = 1;//记得初始化0点为0
	
	queue<int> q;
	q.push(root);
	while(q.size())
	{
		int ver = q.front();
		q.pop();
		for(auto j : g[ver])
		{
			if(depth[j] > depth[ver] + 1)
			{
				depth[j] = depth[ver] + 1;
				q.push(j);
				
				f[j][0] = ver;
				for(int k = 1; k < M; k ++)
				{
					f[j][k] = f[f[j][k - 1]][k - 1];
				}
			}
		}
	} 
}

int lca(int a, int b)
{
	if(depth[a] < depth[b])swap(a, b);

	for(int k = M - 1; k >= 0; k --)
	{
	    if(depth[f[a][k]] >= depth[b])
	        a = f[a][k];
	}
	
	if(a == b)return a;
	
	for(int k = M - 1; k >= 0; k --)
	{
		if(f[a][k] != f[b][k])
		{
			a = f[a][k];
			b = f[b][k];
		}
	}
	return f[a][0];
}

int main()
{
	IOS
	cin >> n;
	for(int i = 0; i < n; i ++)
	{
		int a, b;
		cin >> a >> b;
		if(b == -1)root = a;
		else
		{
			g[a].push_back(b);
			g[b].push_back(a);
		}
	}
	
	bfs();
	
	cin >> m;
	while(m --)
	{
		int a, b;
		cin >> a >> b;
		int ver = lca(a, b);
		if(ver == a)cout << 1 << endl;
		else if(ver == b) cout << 2 << endl;
		else cout << 0 << endl;
	}
	
	
	return 0;
}

离线lca(tarjan)

O(n+m)

理论时间复杂度比在线做法低,但实战不一定比倍增lca快。

向上标记法的优化

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair<int, int> PII;
typedef long long ll;

const int N = 10010, M = 20010;

int n, m;
int h[N], e[M], w[M], ne[M], idx;
int res[M];
int dist[N];
int p[N];
int st[N];
vector<PII> query[N];

void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int u, int fa)
{
	for(int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if(j == fa)continue;
		dist[j] = dist[u] + w[i];
		dfs(j, u);
	}
}

int find(int x)
{
	if(p[x] != x)return p[x] = find(p[x]);
	return x;
}

void tarjan(int u)
{
	st[u] = 1;//这个点正在搜 
	
	for(int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if(!st[j])//没搜过的点 
		{
			tarjan(j);
			p[j] = u;
		}
	}
	
	for(auto x : query[u])
	{
		int ver = x.first, id = x.second;
		if(st[ver] == 2)
		{
			int anc = find(ver);
			res[id] = dist[u] + dist[ver] - 2 * dist[anc];
		}
	}
	
	st[u] = 2;//这个点搜完了 
}

int main()
{
	IOS
	cin >> n >> m;
	memset(h, -1, sizeof h);
	for(int i = 1; i < n; i ++)
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c), add(b, a, c);
	}
	
	dfs(1, -1);
	
	for(int i = 0; i < m; i ++)
	{
		int a, b;
		cin >> a >> b;
		if(a != b)
		{
			query[a].push_back({b, i});
			query[b].push_back({a, i});
		}
	}
	
	for(int i = 1; i <= n; i ++)p[i] = i;
	
	tarjan(1);
	
	for(int i = 0; i < m; i ++)cout << res[i] << endl;
	
	return 0;
}

lca求严格次小生成树

所以要遍历每一条不在最小生成树里的边,加进去后再删去新生成环的除这条边外的最大值(还要<这条边) (因为新加的边一定>=这个环里的每条边,不然这个生成树就不是最小生成树了)

求不严格次小生成树的话直接 删去新生成环的除这条边外的最大值 即可,不用严格小于

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair<int, int> PII;
typedef long long ll;

const int N = 100010, M = 200010, INF = 0x3f3f3f3f;

int n, m;
struct Node
{
	int a, b, w;
	bool used;
	
	bool operator< (const Node &t) const
	{
		return w < t.w;
	}
} edges[N * 3];
int h[N], e[M], w[M], ne[M], idx;
int p[N];
int depth[N], fa[N][17], d1[N][17], d2[N][17];
ll sum;

int find(int x)
{
	if(p[x] != x)return p[x] = find(p[x]);
	return x;
}

void kruskal()
{
	for(int i = 0; i < m; i ++)
	{
		int a = find(edges[i].a), b = find(edges[i].b), w = edges[i].w;
		if(a != b)
		{
			sum += w;
			edges[i].used = true;
			p[a] = b;
		}
	}
}

void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

void build()
{
	memset(h, -1, sizeof h);
	for(int i = 0; i < m; i ++)
	{
		if(edges[i].used)
		{
			int a = edges[i].a, b = edges[i].b, w = edges[i].w;
			add(a, b, w), add(b, a, w);
		}
	}
}

void bfs()
{
	memset(depth, 0x3f, sizeof depth);
	depth[0] = 0, depth[1] = 1;
	queue<int> q;
	q.push(1);
	
	while(q.size())
	{
		int ver = q.front();
		q.pop();
		
		for(int i = h[ver]; ~i; i = ne[i])
		{
			int j = e[i];
			if(depth[j] > depth[ver] + 1)
			{
				depth[j] = depth[ver] + 1;
				q.push(j);
				
				fa[j][0] = ver, d1[j][0] = w[i], d2[j][0] = -INF;
				for(int k = 1; k <= 16; k ++)
				{
					int anc = fa[j][k - 1];
					fa[j][k] = fa[anc][k - 1];
					
					int distance[4] = {d1[j][k - 1], d2[j][k - 1], d1[anc][k - 1], d2[anc][k - 1]};
					//次大值在这四个数中选即可
					int dist1 = -INF, dist2 = -INF;
					for(int u = 0; u < 4; u ++)
					{
						if(distance[u] > dist1)dist2 = dist1, dist1 = distance[u];
						else if(distance[u] > dist2 && distance[u] != dist1)dist2 = distance[u];
					}
					d1[j][k] = dist1, d2[j][k] = dist2;
				}
			}
		}
	}
}

ll lca(int a, int b, int w)
{
	if(depth[a] < depth[b])swap(a, b);
	
	vector<int> distance;
	for(int i = 16; i >= 0; i --)
	{
		if(depth[fa[a][i]] >= depth[b])
		{
			distance.push_back(d1[a][i]);
			distance.push_back(d2[a][i]);
			a = fa[a][i];
		}
	}
	
	if(a != b)
	{
		for(int i = 16; i >= 0; i --)
		{
			if(fa[a][i] != fa[b][i])
			{
				distance.push_back(d1[a][i]);
				distance.push_back(d2[a][i]);
				distance.push_back(d1[b][i]);
				distance.push_back(d2[b][i]);
				a = fa[a][i], b = fa[b][i];
			}
		}
		//别忘了把最后一步也加进去 
		distance.push_back(d1[a][0]);
		distance.push_back(d1[b][0]);
	}
	
	int dist1 = -INF, dist2 = -INF;
	for(auto d : distance)
	{
		if(d > dist1)dist2 = dist1, dist1 = d;
		else if(d > dist2 && d != dist1)dist2 = d;
	}
	
	if(w > dist1)return w - dist1;
	return w - dist2;
} 

int main()
{
	IOS
	cin >> n >> m;
	for(int i = 0; i < m; i ++)
	{
		int a, b, w;
		cin >> a >> b >> w;
		edges[i] = {a, b, w, false};
	}
	sort(edges, edges + m);
	
	for(int i = 1; i <= n; i ++)p[i] = i;
	kruskal();
	
	build();//建边 
	bfs();//lca初始化 
	
	ll ans = 2e18;
	for(int i = 0; i < m; i ++)
	{
		if(!edges[i].used)
		{
			int a = edges[i].a, b = edges[i].b, w = edges[i].w;
			ans = min(ans, sum + lca(a, b, w));
			//lca返回w - 最大值
			//最大值一定<=w,但如果等于w的话就不是"严格"次小生成树了
			//如果全部边都=w,那就返回一个正无穷 
			//可以考虑初始化为-INF,这样如果没有严格次大值
			//且全部边都=w时就会返回w - (-INF) 
		}
	}
	
	cout << ans;
	
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值