CSP-S模版-图论

模版来源于洛谷CSP-S模版赛

P3379 【模板】最近公共祖先(LCA)

原题链接

题意

给定一棵树,求树上两点的最近公共祖先(LCA)

最近公共祖先简称 LCA(Lowest Common Ancestor).两个节点的最近公共祖先,就是这两个点的公共祖先里面,离根最远的那个.

做法

倍增/Tarjan

由于我不会Tarjan,因此做法采取倍增

\(fa_{i,j}\) 表示 \(i\) 的第 \(2^{j}\) 级祖先是谁

然后给两个点,先将两个点跳到同一高度,此时如果是一个点的话就直接返回,不是的话则二分即可

注意是一个点的话一定要返回,因为二分的是深度最小的两个点使得两个点的父亲节点不是同一个点,然后返回父亲节点。此时如果不返回后面就会返回成 LCA 的 father

代码

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define int128 __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
#define finf 0xcfcfcfcf
#define FINF 0xcfcfcfcfcfcfcfcf
#define endl '\n'
#define lowbit(x) (x & -x)
#define ls (u<<1)
#define rs (u<<1|1)
#define eps 1e-9
#define mem(x) memset(x,0x3f,sizeof(x));
#define fmem(x) memset(x,0xcf,sizeof(x));
using namespace std;
const int maxn=5e5+5;
const int maxm=1e6;
const int mod=998244353;
int n,m,x,y,t,root;
int pw(int a,int b){

	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}

	return ans;
}
int fa[maxn][21];
int dep[maxn];
vector<int>vc[maxn];
void dfs(int u,int f){
	fa[u][0]=f;
	dep[u]=dep[f]+1;
	for(auto v:vc[u]){
		if(v==f)continue;
		dfs(v,u);
	}
	return ;
}
int LCA(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	int o=dep[x]-dep[y];
	for(int i=20;i>=0;i--)if((1<<i)&o)x=fa[x][i];
	if(x==y)return x;
	for(int i=20;i>=0;i--)if(fa[x][i]==fa[y][i])continue;else x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m>>root;
    for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		vc[u].push_back(v);
		vc[v].push_back(u);
	}
	dfs(root,0);
	for(int j=1;j<=20;j++)for(int i=1;i<=n;i++)fa[i][j]=fa[fa[i][j-1]][j-1];
while(m--){
	int u,v;
	cin>>u>>v;
	cout<<LCA(u,v)<<endl;
}
    
    return 0;
}

后记

好好看的代码


P3366 【模板】最小生成树

原题链接

前言

神秘题目,一生之敌,详见CSP-S2025T2

题意

求一个图的最小生成树

做法

这道题不是最短路 (dijkstra) !!!!!!!
卡掉dij的构造就给一个很长的边就行了

对于每条边按从小到大排序,然后用个并查集从小到大选即可
正确性的话因为每次都选最小的,如果有一条边能选但不选的话就只会从后面也就是更大的边把两个联通块合并起来,显然不是更优

本题图不一定联通

代码

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define int128 __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
#define finf 0xcfcfcfcf
#define FINF 0xcfcfcfcfcfcfcfcf
#define endl '\n'
#define lowbit(x) (x & -x)
#define ls (u<<1)
#define rs (u<<1|1)
#define eps 1e-9
#define mem(x) memset(x,0x3f,sizeof(x));
#define fmem(x) memset(x,0xcf,sizeof(x));
using namespace std;
const int maxn=2e5+5;
const int maxm=1e6;
const int mod=998244353;
int n,m,x,y,t;
int pw(int a,int b){

	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}

	return ans;
}
int fa[maxn];
int cnt,ans;
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void hb(int x,int y){
	x=find(x),y=find(y);
	if(x==y)return ;
	fa[x]=y;
}
struct cxh{
	int u,v,w;
}a[maxn];
bool cmp(cxh x,cxh y){
	return x.w<y.w;
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		a[i]={u,v,w};
	}
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=m;i++){
		auto [u,v,w]=a[i];
		u=find(u),v=find(v);
		if(u==v)continue;
		cnt++;
		fa[u]=v;
		ans+=w;
		if(cnt==n-1)break;
	}
	if(cnt==n-1)cout<<ans<<endl;
    else cout<<"orz"<<endl;
    
    return 0;
}

P4779 【模板】单源最短路径(标准版)

原题链接

题意

求一个点到其他每个点的最短路的长度

做法

dijkstra
具体怎么做就不说了,这辈子都忘不掉吧。

dijkstra的细节

  • dij本身是 \(O(n^2)\)
  • dij加上优先队列或者说堆优化后是 \(O((n+m)log m)\)
  • dij不能处理负权边,因为我们在从这个点出发一次之后就会把这个点锁了,但由于负权边会导致可能会有现在更长之后更短的距离到这里,也就是更短的路到的时候这个点已经被锁了无法更新后面的
  • 注意重载运算符的写法

代码

// __   _________ ____ _______
// \ \ / /__   __|  _ \__   __|
//  \ V /   | |  | |_) | | |
//   > <    | |  |  _ <  | |
//  / . \   | |  | |_) | | |
// /_/ \_\  |_|  |____/  |_|
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define int128 __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
#define finf 0xcfcfcfcf
#define FINF 0xcfcfcfcfcfcfcfcf
#define endl '\n'
#define lowbit(x) (x & -x)
#define ls (u<<1)
#define rs (u<<1|1)
#define eps 1e-9
#define mem(x) memset(x,0x3f,sizeof(x));
#define fmem(x) memset(x,0xcf,sizeof(x));
using namespace std;
const int maxn=2e5+5;
const int mod=1e9+7;
int n,m,x,y,t,s;
vector<pair<int,int>>vc[maxn];
int dp[maxn];
int b[maxn];
struct cxh{
	int t,w;
	bool operator< (cxh x)const{
		return w>x.w;
	}
};
priority_queue<cxh>q;
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m>>s;
while(m--){
	int u,v,w;
	cin>>u>>v>>w;
	vc[u].push_back({v,w});
}
	for(int i=1;i<=n;i++)dp[i]=INF;
	q.push({s,0});
	dp[s]=0;
	while(!q.empty()){
		cxh p=q.top();
		q.pop();
		int u=p.t,w=p.w;
		if(b[u])continue;
		b[u]=1;
		for(auto o:vc[u]){
			int v=o.first,ww=o.second;
			if(dp[v]>dp[u]+ww){
				dp[v]=dp[u]+ww;
				q.push({v,dp[v]});
			}
		}
	}
	for(int i=1;i<=n;i++)cout<<dp[i]<<" ";
	cout<<endl;
    
    return 0;
}

B3644 【模板】拓扑排序 / 家谱树】

原题链接

题意

对于一张有向图输出拓扑序

做法

其实题目背景比题意更好理解,或者说直接给出了做法
等到这个点的没有入度的时候就输出然后push进队列就行了

代码

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define int128 __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
#define finf 0xcfcfcfcf
#define FINF 0xcfcfcfcfcfcfcfcf
#define endl '\n'
#define lowbit(x) (x & -x)
#define ls (u<<1)
#define rs (u<<1|1)
#define eps 1e-9
#define mem(x) memset(x,0x3f,sizeof(x));
#define fmem(x) memset(x,0xcf,sizeof(x));
using namespace std;
const int maxn=2e5+5;
const int maxm=1e6;
const int mod=998244353;
int n,m,x,y,t;
int pw(int a,int b){

	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}

	return ans;
}
vector<int>vc[maxn];
int d[maxn];
queue<int>q;
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
		int u;
		while(1){
			cin>>u;
			if(u==0)break;
			vc[i].push_back(u);
			d[u]++;
		}
	}
	for(int i=1;i<=n;i++)if(d[i]==0)q.push(i),cout<<i<<" ";
while(!q.empty()){
	int u=q.front();q.pop();
	for(auto v:vc[u]){
		d[v]--;
		if(d[v]==0)cout<<v<<" ",q.push(v);
	}
}
    
    return 0;
}

P5960 【模板】差分约束

原题链接

题意

给定一组不等式,求这组不等式的一组解

做法

等我学下 SPFA

代码



欧拉路径

原题链接

题意

求一个图字典序最小的欧拉路径

欧拉路径(Eulerian path)是经过图中每条边恰好一次的路径,欧拉回路(Eulerian circuit)是经过图中每条边恰好一次的回路. 如果一个图中存在欧拉回路

做法

posted @ 2026-10-26 17:07  XTBT  阅读(5)  评论(0)    收藏  举报