【模板】圖論

ft61發表於2024-07-28

圖論

  • \(k\) 短路

  • 圓方樹

int n,nn;
struct {
	Vi e[N];
	void adde(int x,int y) { e[x].pb(y), e[y].pb(x); }
} tr;
struct {
	int ind,dfn[N],low[N];
	bool cut[N];
	Vi e[N];
	void tarjan(int rt,int u) {
		static int t,s[N];
		dfn[u] = low[u] = ++ind, s[++t] = u;
		int son = 0;
		for(int v : e[u])
			if( dfn[v] ) ckmin(low[u],dfn[v]);
			else {
				tarjan(rt,v), ckmin(low[u],low[v]);
				if( dfn[u] <= low[v] ) {
					if( u != rt || son++ ) cut[u] = 1;
					++nn, tr.adde(nn,u);
					do tr.adde(nn,s[t]); while( v != s[t--] );
				}
			}
	}
	void main() {
		For(i,1,n) if( !dfn[i] ) tarjan(i,i);
	}
} G;
  • SCC 縮點
int nn,ind,dfn[N],low[N],id[N];
Vi scc[N],ee[N];
void tarjan(int u) {
	static int t,s[N];
	dfn[u] = low[u] = ++ind, s[++t] = u, id[u] = -1;
	for(auto [v,w] : e[u])
		if( !dfn[v] ) tarjan(v), ckmin(low[u], low[v]);
		else if( !~id[v] ) ckmin(low[u], dfn[v]);
	if( dfn[u] == low[u] ) {
		++nn;
		do id[s[t]] = nn, scc[nn].pb(s[t]); while( u != s[t--] );
	}
}
signed main() {
	For(i,1,n) if( !dfn[i] ) tarjan(i);
	For(u,1,n) for(int v : e[u]) if( id[u] != id[v] ) ee[id[u]].pb(id[v]);
}

  • Prufer 序列

\(n\) 個點的完全圖的生成樹 \(\leftrightarrow\) 長度為 \(n-2\) 值域為 \([1,n]\) 的序列

// tree -> prufer
For(i,1,n-1) ++deg[fa[i]];
for(int i = 1, u = 1; i <= n-2; ++i, ++u) {
	while( deg[u] ) ++u; p[i] = fa[u];
	for(; i <= n-2 && !--deg[p[i]] && p[i] < u; ++i) p[i+1] = fa[p[i]];
}
// prufer -> tree
For(i,1,n-2) ++deg[p[i]]; p[n-1] = n;
for(int i = 1, u = 1; i < n; ++i, ++u) {
	while( deg[u] ) ++u; fa[u] = p[i];
	for(; i < n && !--deg[p[i]] && p[i] < u; ++i) fa[p[i]] = p[i+1];
}
  • ST 表 LCA
int n,rt,dfn[N],st[][N];
Vi e[N];
int _min(int u,int v) { return dfn[u]<dfn[v] ? u : v; }
void dfs(int u,int fa) {
	static int ind;
	dfn[u] = ++ind, st[0][ind] = fa;
	for(int v : e[u]) if( v != fa ) dfs(v,u);
}
int lca(int u,int v) {
	if( u == v ) return u;
	if( (u=dfn[u]) > (v=dfn[v]) ) swap(u,v);
	int k = __lg(v-u++);
	return _min(st[k][u],st[k][v-(1<<k)+1]);
}
signed main() {
	dfs(rt,0);
	For(i,1,) For(j,1,n-(1<<i)+1) st[i][j] = _min(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
  • tarjan LCA

  • 虛樹

namespace T {
int dfn[N];
int lca(int x,int y) {}
}
namespace VT {
using namespace T;
bool is1[N];
Vi v2,e[N];
void adde(int x,int y) { e[x].pb(y), e[y].pb(x); }
void main(Vi &v1) {
	{ // build
		v1.resize(read()); for(int &i : v1) cin>>i, is1[i] = 1;
		auto cmp = [](int x,int y) { return dfn[x] < dfn[y]; };
		v2 = v1, sort(all(v2),cmp);
		Rep(i,1,sz(v1)) v2.pb(lca(v2[i-1],v2[i]));
		v2.pb(1), sort(all(v2),cmp), v2.erase(unique(all(v2)),v2.end());
		Rep(i,1,sz(v2)) adde(lca(v2[i-1],v2[i]),v2[i]);
	}
	{ // clear
		for(int i : v1) is1[i] = 0;
		for(int i : v2) e[i].clear();
	}
}
}

網路流

  • 最大流/最小割

流量 long long

namespace flow {
const int N = ;
const LL inf = 0x3f3f3f3f3f3f3f3f;
int fn,S,T,head[N],dis[N];
struct Edge {
	int to,b; LL c;
	Edge(int to=0,int b=0,LL c=0):to(to),b(b),c(c){}
}; vector<Edge> e[N];
void init() { S = 1, T = fn = 2; }
void addedge(int x,int y,LL z) { e[x].pb(y,sz(e[y]),z), e[y].pb(x,sz(e[x])-1,0); }
bool bfs() {
	static int l,r,q[N];
	memset(head+1,0,sizeof(int)*fn), memset(dis+1,0,sizeof(int)*fn);
	dis[S] = 1, q[l=r=1] = S;
	while( l <= r ) {
		int u = q[l++];
		for(auto [v,b,c] : e[u]) if( c && !dis[v] )
			{ dis[v] = dis[u]+1, q[++r] = v; if( v == T ) return 1; }
	}
	return 0;
}
LL dfs(int u,LL f) {
	if( u == T ) return f;
	LL use = 0;
	for(int &i = head[u]; i < sz(e[u]); ++i)
		if(auto &[v,b,c] = e[u][i]; c && dis[u]+1 == dis[v] ) {
			if( LL g = dfs(v,min(c,f-use)) ) {
				c -= g, e[v][b].c += g;
				if( (use+=g) == f ) return f;
			} else dis[v] = 0;
		}
	return use;
}
LL dinic() { LL mf=0; while( bfs() ) mf += dfs(S,inf); return mf; }
} using flow::fn; using flow::S; using flow::T; using flow::addedge;
  • 最小費用最大流

流量 int,費用 long long

namespace flow {
const int N = , inf = 0x3f3f3f3f; const LL infl = 0x3f3f3f3f3f3f3f3f;
int fn,S,T,mf,mc,head[N],dep[N]; LL dis[N];
bitset<N> vis;
struct Edge {
	int to,b,c; LL w;
	Edge(int to=0,int b=0,int c=0,LL w=0):to(to),b(b),c(c),w(w){}
}; vector<Edge> e[N];
void init() { S = 1, T = fn = 2; }
void addedge(int x,int y,int z=1,LL w=0)
	{ e[x].pb(y,sz(e[y]),z,w), e[y].pb(x,sz(e[x])-1,0,-w); }
bool spfa() {
	static queue<int> q; 
	memset(head+1,0,sizeof(int)*fn), memset(dis+1,0x3f,sizeof(dis[0])*fn);
	dis[S] = 0, q.emplace(S);
	while( q.size() ) {
		int u = q.front(); q.pop(); vis[u] = 0;
		for(auto& [v,b,c,w] : e[u]) if( c && dis[u]+w < dis[v] ) {
			dis[v] = dis[u]+w, dep[v] = dep[u]+1;
			if( !vis[v] ) vis[v] = 1, q.emplace(v);
		}
	}
	return dis[T] < infl;
}
int dfs(int u,int f) {
	if( u == T ) return f;
	int use = 0;
	for(int &i = head[u]; i < sz(e[u]); ++i) {
		auto& [v,b,c,w] = e[u][i];
		if( c && dis[u]+w == dis[v] && dep[u]+1 == dep[v] ) {
			if( int g = dfs(v,min(c,f-use)) ) {
				c -= g, e[v][b].c += g;
				if( (use+=g) == f ) break;
			} else dis[v] = -1;
		}
	}
	return use;
}
void dinic() { while( spfa() ) { int f = dfs(S,inf); mf += f, mc += f * dis[T]; } }
} using flow::fn; using flow::S; using flow::T; using flow::addedge;
  • 負圈最小費用最大流

有源匯上下界

namespace flow {
const int N = , inf = 0x3f3f3f3f; const LL infl = 0x3f3f3f3f3f3f3f3f;
int s,t,fn,S,T,mf,mc,dlt[N],head[N],dep[N]; LL dis[N];
bitset<N> vis;
struct Edge {
	int to,b,c; LL w;
	Edge(int to=0,int b=0,int c=0,LL w=0):to(to),b(b),c(c),w(w){}
}; vector<Edge> e[N];
void addedge(int x,int y,int z=1,LL w=0) {
	auto adde=[](int x,int y,int z,LL w)
		{ e[x].pb(y,sz(e[y]),z,w), e[y].pb(x,sz(e[x])-1,0,-w); };
	if( w >= 0 ) adde(x,y,z,w);
	else mc += z*w, adde(y,x,z,-w), dlt[x] -= z, dlt[y] += z;
}

void main() {
	For(i,1,fn)
		if( dlt[i] > 0 ) addedge(S,i,dlt[i]);
		else if( dlt[i] < 0 ) addedge(i,T,-dlt[i]);
	addedge(t,s,inf);
	dinic();
	mf = 0, S = s, T = t, dinic();
}
} using flow::fn; using flow::S; using flow::T; using flow::addedge;

相關文章