【模板】图论

最小环

Floyd 求最小环+输路径

模板指路

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

int a[105][105],d[105][105],pa[105][105],ans[105];
int mi=1e9,co;

void get_pa(int x,int y){
	if(!pa[x][y])	return;
	get_pa(x,pa[x][y]);
	ans[++co]=pa[x][y];
	get_pa(pa[x][y],y);
}

signed main(){
	
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)	a[i][j]=d[i][j]=1e9;
	for(int i=1;i<=m;++i){
		int x,y,z;cin>>x>>y>>z;
		a[x][y]=a[y][x]=d[x][y]=d[y][x]=min(z,d[x][y]);
	}
	
	for(int k=1;k<=n;++k){
		for(int i=1;i<k;++i)
			for(int j=i+1;j<k;++j)
				if(d[i][j]+a[i][k]+a[k][j]<mi){
					mi=d[i][j]+a[i][k]+a[k][j];
					co=0;
					get_pa(i,j),ans[++co]=j,ans[++co]=k,ans[++co]=i;
				}
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				if(d[i][j]>d[i][k]+d[k][j]){
					d[i][j]=d[i][k]+d[k][j];
					pa[i][j]=k;
				}
	}
	if(mi==1e9)	cout<<"No solution.";
	else	for(int i=1;i<=co;++i)	cout<<ans[i]<<" ";
	
	return 0;
}

LCA

倍增求LCA

模板指路

点击查看代码
const int N=5e5+5;
int fa[N][30],d[N];
int s=1;
vector<int> ed[N];

void bfs(){
    queue<int> q;
    q.push(s);d[0]=-1e9;
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=0;i<(int)ed[x].size();++i){
            int y=ed[x][i];
            if(y==s || d[y])    continue;
            d[y]=d[x]+1;q.push(y);
            fa[y][0]=x;
            for(int j=1;j<=20;++j)
                fa[y][j]=fa[fa[y][j-1]][j-1];
        }
    }
}
int lca(int x,int y){
    if(d[y]>d[x])   swap(x,y);
    for(int i=20;i>=0;--i)
        if(d[fa[x][i]]>=d[y])   x=fa[x][i];
    if(x==y)    return x;
    for(int i=20;i>=0;--i)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

Tarjan

模板指路

点击查看代码
#include<bits/stdc++.h>
using namespace std;
//桥一定要去重边!!!
const int N=2e3+5,M=2e6+5;
int dfn[N],low[N],cnt;
int h[N],nxt[M],ver[M],st[M],co;
bool a[N][N];
int n,m;
set<pair<int,int> > s;

void add(int x,int y){
	nxt[++co]=h[x],h[x]=co,ver[co]=y,st[co]=x;
}

int tarjan(int x,int fa){
	dfn[x]=++cnt,low[x]=cnt;

	for(int i=h[x];i;i=nxt[i]){
		int y=ver[i];
		if(y==fa)	continue;
		if(dfn[y])	low[x]=min(low[x],low[y]);
		else	low[x]=min(low[x],tarjan(y,x));

		if(low[y]>dfn[x])	s.insert(make_pair(min(st[i],ver[i]),max(st[i],ver[i])));
	}
	return low[x];
}

int main(){

	int n,m;cin>>n>>m;
	for(int i=1;i<=m;++i){
		int x,y;cin>>x>>y;
		if(a[x][y]) continue;
		add(x,y),add(y,x),a[x][y]=a[y][x]=1;
	}

	for(int i=1;i<=n;++i)
		if(!dfn[i]) tarjan(i,0);

	cout<<s.size()<<endl;
	for(auto it:s)
		cout<<it.first<<" "<<it.second<<endl;

	return 0;
}

割点

模板指路

点击查看代码
#include<bits/stdc++.h>
using namespace std;
//割点不用去重边!!!
const int N=2e4+5,M=1e5+5;
vector<int> ed[N];
int dfn[N],low[N],cnt;
set<int> s;

int tarjan(int x,int fa){
	low[x]=dfn[x]=++cnt;
	int co=0;
	for(int i=0;i<ed[x].size();++i){
		int y=ed[x][i];
		if(y==fa)	continue;
		if(!dfn[y]){
			low[x]=min(low[x],tarjan(y,x)),++co;
			if(fa && low[y]>=dfn[x])	s.insert(x);
		}
		else	low[x]=min(low[x],dfn[y]);
	}
	if(!fa && co>1)	s.insert(x);
	return low[x];
}

int main(){

	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int x,y;cin>>x>>y;
		ed[x].push_back(y);
		ed[y].push_back(x);
	}

	for(int i=1;i<=n;++i)
		if(!dfn[i])	tarjan(i,0);

	cout<<s.size()<<endl;
	for(auto it:s)	cout<<it<<" ";

	return 0;
}

缩点、Tarjan 求强联通分量

模板指路

点击查看代码
//Luogu P3387 【模板】缩点
#include<bits/stdc++.h>
using namespace std;

const int N=1e4+5,M=1e5+5;
vector<int> ed[N],e[N];
int dfn[N],low[N],st[N],top,cnt;
bool vi[N];
int a[N],ans;
int w[N],co,d[N],f[N],dp[N];
int n,m;
struct edge{
	int x,y;
}edg[M];

int tarjan(int x){
	dfn[x]=low[x]=++cnt;
	st[++top]=x;
	vi[x]=1;
	for(int i=0;i<ed[x].size();++i){
		if(vi[ed[x][i]])	low[x]=min(low[x],low[ed[x][i]]);
		if(!dfn[ed[x][i]])	low[x]=min(low[x],tarjan(ed[x][i]));
	}
	if(dfn[x]==low[x]){
		int sum=a[x];
		while(top && st[top]!=x)
			f[st[top]]=x,sum+=a[st[top]],vi[st[top]]=0,--top;
		--top,vi[x]=0,f[x]=x;
		w[x]=sum;
	}
	return low[x];
}

void topo(){
	queue<int> q;
	for(int i=1;i<=n;++i)
		if(f[i]==i && !d[i])	q.push(i),dp[i]=w[i];
	while(q.size()){
		int x=q.front();q.pop();
		ans=max(ans,dp[x]);
		for(int i=0;i<e[x].size();++i){
			int y=e[x][i];
			dp[y]=max(dp[y],dp[x]+w[y]);
			d[y]--;
			if(!d[y])	q.push(y);
		}
	}
}

int main(){
	
	cin>>n>>m;
	for(int i=1;i<=n;++i)	cin>>a[i];
	for(int i=1;i<=m;++i){
		cin>>edg[i].x>>edg[i].y;
		ed[edg[i].x].push_back(edg[i].y);
	}
	
	for(int i=1;i<=n;++i)
		if(!dfn[i])	tarjan(i);
	
	for(int i=1;i<=m;++i)
		if(f[edg[i].x]!=f[edg[i].y]){
			d[f[edg[i].y]]++;
			e[f[edg[i].x]].push_back(f[edg[i].y]);
		}
	
	topo();
	
	cout<<ans;
	
	return 0;
}

基环树

拓扑排序找环(无向图)

模板
void topo(int x){
    queue<int> q;
    for(int i=1;i<=n;++i)
        if(d[i]==1)	q.push(cal[x][i]);
    while(q.size()){
        int y=q.front();q.pop();
        for(int i=0;i<ed[y].size();++i){
            int z=ed[y][i];
            if(d[z]<=1)	continue;
            --d[z];
            if(d[z]==1)	q.push(z);
        }
    }

    for(int i=1;i<=n;++i)
		if(d[i]>1)	++cnt;
}

二分图

二分图最大匹配(匈牙利算法)

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=505,M=5e4+5;
int h[N],nxt[M],en[M],co;
bool v[N];
int match[N];

void add(int a,int b){
	nxt[++co]=h[a],en[co]=b,h[a]=co;
}

bool dfs(int x){
	for(int i=h[x];i;i=nxt[i])
		if(!v[en[i]]){
			v[en[i]]=1;
			if(!match[en[i]] || dfs(match[en[i]])){
				match[en[i]]=x;
				return 1;
			}
		}
	return 0;
}

int main(){
	
	int n,m,e;
	cin>>n>>m>>e;
	for(int i=1;i<=e;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);
	}
	
	int ans=0;
	for(int i=1;i<=n;i++){
		memset(v,0,sizeof v);
		if(dfs(i))	ans++;
	}
	
	cout<<ans;
	
	return 0;
}

网络流

最大流、最小费用最大流、最小割

Dinic(最大网络流=最小割)

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5,M=2e3+5,INF=0x3f3f3f3f;
int nxt[N],h[M],w[N],ver[N],co=1;
int n,m,s,t;
int d[M],no[M];

inline int read(){
	int sum=0,f=1;char a=getchar();
	while(a<'0' || a>'9'){if(a=='-') 	f=-1;a=getchar();}
	while(a>='0' && a<='9') 	sum=sum*10+a-'0',a=getchar();
	return sum*f;
}


void add(int x,int y,int z){
	nxt[++co]=h[x],h[x]=co,ver[co]=y,w[co]=z;
	nxt[++co]=h[y],h[y]=co,ver[co]=x,w[co]=0;
}

bool bfs(){
	memset(d,0,sizeof d);
	queue<int> q;
	q.push(s);
	d[s]=1,no[s]=h[s];
	while(q.size()){
		int x=q.front();q.pop();
		for(int i=h[x];i;i=nxt[i]){
			int y=ver[i];
			if(!d[y] && w[i]){
				d[y]=d[x]+1;
				no[y]=h[y];
				q.push(y);
				if(y==t)	return 1;
			}
		}
	}
	return 0;
}

int dfs(int x,int flow){
	if(x==t)	return flow;
	int rest=flow,k,i;
	for(i=no[x];i && rest;i=nxt[i]){
		int y=ver[i];
		if(d[y]==d[x]+1 && w[i]){
			k=dfs(y,min(rest,w[i]));
			if(!k)	d[y]=0;
			w[i]-=k;
			w[i^1]+=k;
			rest-=k;
		}
	}
	no[x]=i;
	return flow-rest;
}

int main(){
	
	n=read(),m=read();
	s=1,t=n;
	for(int i=1;i<=m;++i){
		int x=read(),y=read(),z=read();
		add(x,y,z);
	}
	
	int ans=0,flow=0;
	while(bfs())
		while(flow=dfs(1,INF))
		ans+=flow;
	
	cout<<ans<<endl;
	
	return 0;
}

EK(最小费用流)

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=5e3+5,M=1e5+5,INF=0x3f3f3f3f;
int nxt[M],h[N],ver[M],w[M],fl[M];
int n,m,s,t,co=1;//co=1!!!!!!!
bool vi[N];
int pre[N],dis[N];

inline int read(){
	int sum=0,f=1;char a=getchar();
	while(a<'0' || a>'9'){if(a=='-') 	f=-1;a=getchar();}
	while(a>='0' && a<='9') 	sum=sum*10+a-'0',a=getchar();
	return sum*f;
}

void add(int x,int y,int z,int d){
	nxt[++co]=h[x],h[x]=co,ver[co]=y,w[co]=z,fl[co]=d;
	nxt[++co]=h[y],h[y]=co,ver[co]=x,w[co]=-z,fl[co]=0;
}

bool spfa(){
	memset(dis,0x3f,sizeof dis);
	memset(vi,0,sizeof vi);
	queue<int> q;
	vi[s]=1,dis[s]=0;
	q.push(s);
	while(q.size()){
		int x=q.front();q.pop();
		vi[x]=0;
		for(int i=h[x];i;i=nxt[i]){
			int y=ver[i];
			if(dis[y]>dis[x]+w[i] && fl[i]){
				pre[y]=i;
				dis[y]=dis[x]+w[i];
				if(vi[y])	continue;
				vi[y]=1;
				q.push(y);
			}
		}
	}
	if(dis[t]==INF)	return 0;
	return 1;
}

int main(){
	
	n=read(),m=read(),s=read(),t=read();
	for(int i=1;i<=m;++i){
		int x,y,z,d;
		x=read(),y=read(),d=read(),z=read();
		add(x,y,z,d);
	}
	
	int flow=0,ans=0;
	while(spfa()){
		int p,mi=INF;
		for(int i=t;i!=s;i=ver[p^1]){
			p=pre[i];
			mi=min(mi,fl[p]);
		}
		for(int i=t;i!=s;i=ver[p^1]){
			p=pre[i];
			fl[p]-=mi;
			fl[p^1]+=mi;
			ans+=w[p]*mi;
		}
		flow+=mi;
	}
	
	cout<<flow<<" "<<ans;
	
	return 0;
}

上下界网络流

无源汇上下界可行流

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=5e4+5,INF=0x3f3f3f3f;
int h[N],nxt[N],ver[N],w[N],co=1;
int in[N],out[N];
int c[N],D[N];
int s,t,n,m;
int d[N],no[N];

void add(int x,int y,int z){
	nxt[++co]=h[x],h[x]=co,ver[co]=y,w[co]=z;
	nxt[++co]=h[y],h[y]=co,ver[co]=x,w[co]=0;
}

bool bfs(){
	memset(d,0,sizeof d);
	queue<int> q;
	q.push(s);
	d[s]=1,no[s]=h[s];
	while(q.size()){
		int x=q.front();q.pop();
		for(int i=h[x];i;i=nxt[i]){
			int y=ver[i];
			if(!d[y] && w[i]){
				d[y]=d[x]+1;
				no[y]=h[y];
				q.push(y);
				if(y==t)	return 1;
			}
		}
	}
	return 0;
}

int dfs(int x,int flow){
	if(x==t)	return flow;
	int rest=flow,k,i;
	for(i=no[x];i && rest;i=nxt[i]){
		int y=ver[i];
		if(d[y]==d[x]+1 && w[i]){
			k=dfs(y,min(rest,w[i]));
			if(!k)	d[y]=0;
			w[i]-=k;
			w[i^1]+=k;
			rest-=k;
		}
	}
	no[x]=i;
	return flow-rest;
}

int main(){
	
	int a,b;
	cin>>n>>m;
	s=n+1,t=s+1;
	for(int i=1;i<=m;++i){
		cin>>a>>b>>c[i]>>D[i];
		add(a,b,D[i]-c[i]);
		in[b]+=c[i],out[a]+=c[i];
	}
	
	int sum=0;
	for(int i=1;i<=n;++i){
		if(in[i]>out[i])	add(s,i,in[i]-out[i]),sum+=in[i]-out[i];
		if(out[i]>in[i])	add(i,t,out[i]-in[i]);
	}
	
	int ans=0,flow=0;
	while(bfs()){
		while(flow=dfs(s,INF))	ans+=flow;
	}
	if(ans!=sum){
		cout<<"NO";
		return 0;
	}
	
	cout<<"YES"<<endl;
	for(int i=2;i<=m*2;i+=2)
		cout<<D[i/2]-w[i]<<endl;
	
	return 0;
}

有源汇有上下界最小流

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5,M=2e3+5,INF=0x3f3f3f3f;
int nxt[N],h[M],w[N],ver[N],co=1;
int n,m,s,t;
int in[N],out[N];
int d[M],no[M];

void add(int x,int y,int z){
	nxt[++co]=h[x],h[x]=co,ver[co]=y,w[co]=z;
	nxt[++co]=h[y],h[y]=co,ver[co]=x,w[co]=0;
}

bool bfs(){
	memset(d,0,sizeof d);
	queue<int> q;
	q.push(s);
	d[s]=1,no[s]=h[s];
	while(q.size()){
		int x=q.front();q.pop();
		for(int i=h[x];i;i=nxt[i]){
			int y=ver[i];
			if(!d[y] && w[i]){
				d[y]=d[x]+1;
				no[y]=h[y];
				q.push(y);
				if(y==t)	return 1;
			}
		}
	}
	return 0;
}

int dfs(int x,int flow){
	if(x==t)	return flow;
	int rest=flow,k,i;
	for(i=no[x];i && rest;i=nxt[i]){
		int y=ver[i];
		if(d[y]==d[x]+1 && w[i]){
			k=dfs(y,min(rest,w[i]));
			if(!k)	d[y]=0;
			w[i]-=k;
			w[i^1]+=k;
			rest-=k;
		}
	}
	no[x]=i;
	return flow-rest;
}

int dinic(){
	int ans=0,flow=0;
	while(bfs()){
		while(flow=dfs(s,INF))	ans+=flow;
	}
	return ans;
}

int main(){
	
	int a,b,c,d;
	cin>>n;
	s=n+1,t=s+1;
	while(cin>>a>>b>>c>>d){
		add(a,b,d-c);
		in[b]+=c,out[a]+=c;
	}
	for(int i=1;i<=n;++i){
		if(in[i]>out[i])	add(s,i,in[i]-out[i]);
		if(out[i]>in[i])	add(i,t,out[i]-in[i]);
	}
	add(n,1,INF);
	
	dinic();
	int t1=INF-w[co^1];
	s=n,t=1,w[co]=0,w[co^1]=0;
	int t2=dinic();
	
	cout<<t1-t2;
	
	return 0;
}
posted @ 2022-08-28 22:21  _yolanda  阅读(33)  评论(0编辑  收藏  举报