模板 - 图论

邻接表(链式前向星)存图

定义

struct Allan{
	int val;
	int to,nxt;
}edge[M];
int idx,head[N];

加边

inline void add(int x,int y,int z)
{
	edge[++idx]={z,y,head[x]};
	head[x]=idx;
	return;
}

遍历出边

for(int i=head[x];i;i=edge[i].nxt)
{
	int y=edge[i].to,z=edge[i].val;
	...
	}
}

树的重心

int size[N],ans=INF;
void DFS(int x)
{
	size[x]=1;
	int tmp=0;
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int y=edge[i].to;
		if(size[y]) continue;
		DFS(y);
		size[x]+=size[y];
		tmp=max(tmp,size[y]);
	}
	tmp=max(tmp,n-size[x]);
	ans=min(ans,tmp);
	return;
}

树的直径

DFS 部分

bool vst[N];
void DFS(int x,int *dep)
{
	vst[x]=true;
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int y=edge[i].to,z=edge[i].val;
		if(vst[y]) continue;
		dep[y]=dep[x]+z;
		DFS(y,dep);
	}
	return;
}

主函数部分

DFS(1,depA);
int farNodeA=1;
for(int i=2;i<=n;i++)
	if(depA[i]>depA[farNodeA]) farNodeA=i;
memset(vst,false,sizeof(vst));
DFS(farNodeA,depB);
int farNodeB=1;
for(int i=2;i<=n;i++)
	if(depB[i]>depB[farNodeB]) farNodeB=i;
printf("%d\n",depB[farNodeB]);

拓扑排序

int deg[N]; //入度
int topOrder[N],topIdx;
queue<int> q;
void TopSort()
{
	for(int i=1;i<=n;i++)
		if(!deg[i]) q.push(i);
	while(!empty())
	{
		int x=q.front(); q.pop();
		topOrder[++topIdx]=x;
		for(int i=head[x];i;i=edge[i].nxt)
		{
			int y=edge[i].to;
			deg[y]--;
			if(!deg[y]) q.push(y);
		}
	}
	return;
}

LCA

倍增求 LCA

注意需要设置根的深度为 \(1\),或设置 \(0\) 的深度为 \(-1\)

遍历部分

void LCA_DFS(int x)
{
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int y=edge[i].to;
		if(dep[y]) continue;
		fa[y][0]=x,dep[y]=dep[x]+1;
		LCA_DFS(y);
	}
	return;
}

预处理部分

inline void LCA_Init()
{
	for(int i=1;i<=logN;i++)
		for(int x=1;x<=n;x++)
			fa[x][i]=fa[fa[x][i-1]][i-1];
	return;
}

在线查询部分

inline int LCA(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=logN;i>=0;i--)
		if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if(x==y) return x;
	for(int i=logN;i>=0;i--)
		if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}

最短路

SPFA

求单元最短路同时判断源点可达负环

int dist[N],len[N];
bool inq[N];
queue<int> q;
bool SPFA(int src)
{
	memset(dist,0x3f,sizeof(dist));
	dist[src]=0; q.push(src);
	while(!q.empty())
	{
		int x=q.front(); q.pop();
		inq[x]=false;
		for(int i=head[x];i;i=edge[i].nxt)
		{
			int y=edge[i].to,z=edge[i].val;
			if(dist[x]+z<dist[y])
			{
				dist[y]=dist[x]+z;
				len[y]=len[x]+1;
				if(len[y]>=n) return true;
				if(!inq[y])
				{
					inq[y]=true;
					q.push(y);
				}
			}
		}
	}
	return false;
}

求单源最短路时判断全局负环

int dist[N],len[N];
bool inq[N];
queue<int> q;
bool SPFA(int src)
{
	memset(dist,0x3f,sizeof(dist));
	dist[src]=0;
	for(int i=1;i<=n;i++)
	{
		inq[i]=true;
		q.push(i);
	}
	while(!q.empty())
	{
		int x=q.front(); q.pop();
		inq[x]=false;
		for(int i=head[x];i;i=edge[i].nxt)
		{
			int y=edge[i].to,z=edge[i].val;
			if(dist[x]+z<dist[y])
			{
				dist[y]=dist[x]+z;
				len[y]=len[x]+1;
				if(len[y]>=n) return true;
				if(!inq[y])
				{
					inq[y]=true;
					q.push(y);
				}
			}
		}
	}
	return false;
}

Dijkstra

堆优化 Dijkstra

#define PII pair<int,int>
LL dis[N];
bool vst[N];
priority_queue<PII,vector<PII>,greater<PII>> pq;
void Dijkstra(int src)
{
	memset(dis,0x3f,sizeof(dis));
	dis[src]=0; pq.push({dis[src],src});
	while(!pq.empty())
	{
		int x=pq.top().second; pq.pop();
		if(vst[x]) continue;
		vst[x]=true;
		for(int i=head[x];i;i=edge[i].nxt)
		{
			int y=edge[i].to,z=edge[i].val;
			if(dis[x]+z<dis[y])
			{
				dis[y]=dis[x]+z;
				if(!vst[y]) pq.push({dis[y],y});
			}
		}
	}
	return;
}

Floyd

memset(g,0x3f,sizeof(g));
... Input Graph ...
for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			g[i][j]=min(g[i][j],g[i][k]+g[k][j]);

传递闭包

for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			g[i][j]|=g[i][k]&g[k][j];

最小生成树

Kruskal

void Kruskal()
{
	uInit();
	sort(raw_edge+1,raw_edge+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		int x=raw_edge[i].x,y=raw_edge[i].y,z=raw_edge[i].z;
		if(uget(x)==uget(y)) continue;
		umerge(x,y);
		ans+=z;
	}
	return;
}

Tarjan

Tarjan 求有向图强连通分量

void Tarjan(int x)
{
	low[x]=dfn[x]=++ti;
	sta[++statop]=x; insta[x]=true;
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int y=edge[i].to;
		if(!dfn[y])
		{
			Tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(insta[y]) low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x])
	{
		scc_tot++;
		int tmp=0;
		do
		{
			tmp=sta[statop--];
			insta[tmp]=false;
			scc[tmp]=scc_tot;
		}while(tmp!=x);
	}
}

Tarjan 求割点

void Tarjan(int x)
{
	low[x]=dfn[x]=++ti;
	sta[++statop]=x;
	bool cut_flag=false,can_go=false;
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int y=edge[i].to;
		if(!dfn[y])
		{
			can_go=true;
			Tarjan(y);
			low[x]=min(low[x],low[y]);
			if(dfn[x]<=low[y])
			{
				if(x!=root || cut_flag) is_cut[x]=true;
				cut_flag=true;
				vdcc_tot++;
				int tmp;
				do
				{
					tmp=sta[statop--];
					vdcc[vdcc_tot].push_back(tmp);
				}while(tmp!=y)
				vdcc[vdcc_tot].push_back(x);
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
	if(x==root && !can_go
		vdcc[++vdcc_tot].push_back(x);
	return;
}

Tarjan 求点双连通分量

int rt;
int dfn[N],low[N],ti;
bool is_cut[N];
int sta[N],top;
int col[N],col_idx;
vector<int> v_dcc[N];
void Tarjan(int x)
{
	low[x]=dfn[x]=++ti;
	sta[++top]=x;
	bool cut_flag=false,can_go=false;
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int y=edge[i].to;
		if(!dfn[y])
		{
			can_go=true;
			Tarjan(y);
			low[x]=min(low[x],low[y]);
			if(low[y]>=dfn[x])
			{
				if(x!=rt || cut_flag)
					is_cut[x]=true;
				cut_flag=true;
				int tmp=0;
				col_idx++;
				do
				{
					tmp=sta[top--];
					col[tmp]=col_idx;
					v_dcc[col_idx].push_back(tmp);
				}while(tmp!=y);
				v_dcc[col_idx].push_back(x);
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
	if(x==rt && !can_go)
	{
		col[x]=++col_idx;
		v_dcc[col_idx].push_back(x);
	}
	return;
}

int main()
{
	...
	for(int i=1;i<=n;i++)
		if(!dfn[i]) Tarjan(rt=i);
	...
}

Tarjan 求割边/桥

#define rev(id) (((id)&1)?((id)+1):((id)-1))
int dfn[N],low[N],ti;
bool is_cut[M];
void Tarjan(int x,int pre)
{
	low[x]=dfn[x]=++ti;
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int y=edge[i].to;
		if(!dfn[y])
		{
			Tarjan(y,i);
			low[x]=min(low[x],low[y]);
			if(low[y]>dfn[x])
				is_cut[i]=is_cut[rev(i)]=true;
		}
		else if(i!=rev(pre))
			low[x]=min(low[x],dfn[y]);
	}
	return;
}
#undef rev

Tarjan 求边双连通分量

...此处为求割边/桥代码...

int col[N],col_idx;
vector<int> e_dcc[N];
void DFS(int x)
{
	e_dcc[col[x]].push_back(x);
	for(int i=head[x];i;i=edge[i].nxt)
	{
		if(is_cut[i]) continue;
		int y=edge[i].to;
		if(col[y]) continue;
		col[y]=col[x];
		DFS(y);
	}
	return;
}

int main()
{
	...
	for(int i=1;i<=n;i++)
		if(!dfn[i]) Tarjan(i,0);
	for(int i=1;i<=n;i++)
		if(!col[i])
		{
			col[i]=++col_idx;
			DFS(i);
		}
	...
}

Tarjan 建立圆方树

int dfn[MAXN], low[MAXN], ti;
int sta[MAXN], stp;
void Tarjan(int x)
{
	low[x] = dfn[x] = ++ti;
	sta[++stp] = x;
	for (int i = G.head[x]; i; i = G.edge[i].nxt)
	{
		int y = G.edge[i].to;
		if (dfn[y])
		{
			low[x] = min(low[x], dfn[y]);
		}
		else
		{
			Tarjan(y);
			low[x] = min(low[x], low[y]);
			if (low[y] == dfn[x])
			{
				vidx++;
				int t = 0;
				while (t != y)
				{
					t = sta[stp--];
					T.add(t, vidx);
					T.add(vidx, t);
				}
				T.add(x, vidx);
				T.add(vidx, x);
			}
		}
	}
	return;
}

二分图

匈牙利算法

bool Hung(int x)
{
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int y=edge[i].to;
		if(vst[i]) continue;
		vst[y]=true;
		if(!match[y] || Hung(match(y)))
		{
			match[y]=x;
			return true;
		}
	}
	return false;
}
posted @ 2024-08-09 19:45  Jerrycyx  阅读(29)  评论(0)    收藏  举报