【模板】图论
图论
-
Johnson 全源最短路
-
短路
A*
可持久化可并堆
// LG2483 【模板】k 短路 / [SDOI2010] 魔法猪学院
const int N = 5e4+5, M = 2e5+5;
int n,m;
double e;
struct Graph {
int mm,head[N],to[M],nxt[M];
double w[M];
void adde(int x,int y,double z)
{ to[++mm] = y, w[mm] = z, nxt[mm] = head[x], head[x] = mm; }
} G,rG;
int ans,pre[N],dfn[N];
bool vis[N];
double dis[N];
struct Node { int id; double dis; };
bool operator < (const Node &x,const Node &y) { return x.dis > y.dis; }
priority_queue<Node> pq;
void dij() {
mem(dis,127,n);
pq.push(Node{n, dis[n]=0 });
while( !pq.empty() ) {
int u = pq.top().id; pq.pop();
if( vis[u] ) continue; vis[u] = 1;
for(int i = rG.head[u], v; v = rG.to[i], i; i = rG.nxt[i])
if( dis[u]+rG.w[i] < dis[v] )
pre[v] = i, pq.push(Node{v, dis[v]=dis[u]+rG.w[i] });
}
}
#define ls(u) t[u].ch[0]
#define rs(u) t[u].ch[1]
int ind,rt[N];
struct Heap { int ch[2],to,dis; double val; } t[N*19];
int newnode(int to=0,double x=0) {
t[++ind].to = to, t[ind].val = x;
return ind;
}
int merge(int u,int v) {
if( !u || !v ) return u | v;
if( t[u].val > t[v].val ) swap(u,v);
int w = newnode();
t[w] = t[u], rs(w) = merge(rs(u),v);
if( t[ls(w)].dis < t[rs(w)].dis ) swap(ls(w),rs(w));
t[w].dis = t[rs(w)].dis+1;
return w;
}
signed main() {
scanf("%d%d%lf",&n,&m,&e);
For(i,1,m) {
int x,y; double z; scanf("%d%d%lf",&x,&y,&z);
if( x == n ) { --i, --m; continue; }
G.adde(x,y,z), rG.adde(y,x,z);
}
dij();
For(i,1,n) dfn[i] = i;
sort(dfn+1,dfn+n+1,[](const int &x,const int &y){return dis[x]<dis[y];});
For(i,1,n) { int u = dfn[i];
rt[u] = rt[G.to[pre[u]]];
for(int j = G.head[u], v; v = G.to[j], j; j = G.nxt[j]) if( j != pre[u] )
rt[u] = merge(rt[u],newnode(v,G.w[j]+dis[v]-dis[u]));
}
if( e < dis[1] ) return write(0), iocl();
e -= dis[1], ++ans;
if( rt[1] ) pq.push(Node{rt[1],t[rt[1]].val});
while( !pq.empty() ) {
Node u = pq.top(); pq.pop();
if( e < dis[1]+u.dis ) break;
e -= dis[1]+u.dis, ++ans;
int v = ls(u.id);
if( v ) pq.push(Node{v,u.dis-t[u.id].val+t[v].val});
v = rs(u.id);
if( v ) pq.push(Node{v,u.dis-t[u.id].val+t[v].val});
v = rt[t[u.id].to];
if( v ) pq.push(Node{v,u.dis+t[v].val});
}
write(ans);
return iocl();
}
- SCC 缩点
int nn,dfn[N],low[N],id[N];
Vi scc[N],ee[N];
void tj(int u) {
static int t,s[N];
dfn[u] = low[u] = ++dfn[0], s[++t] = u, id[u] = -1;
for(auto [v,w] : e[u])
if( !dfn[v] ) tj(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] ) tj(i);
For(u,1,n) for(int v : e[u]) if( id[u] != id[v] ) ee[id[u]].pb(id[v]);
}
- 点双
int n,nn,dfn[N],low[N],id[N];
bool cut[N];
Vi e[N];
struct DCC {
Vi ver;
vector<Pii> edg;
} G[N];
void tj(int rt,int u) {
static Vi stk;
dfn[u] = low[u] = ++dfn[0], stk.pb(u);
int son = 0;
for(int v : e[u]) {
if( !dfn[v] ) {
tj(rt,v), ckmin(low[u],low[v]);
if( dfn[u] <= low[v] ) {
if( u != rt || son++ ) cut[u] = 1;
G[++nn].ver.pb(u), id[u] = nn;
int t; do {
t = stk.back(), stk.pop_back(), G[nn].ver.pb(t), id[t] = nn;
for(int i : e[t]) if( id[i] == nn ) G[nn].edg.pb(t,i);
} while( t != v );
}
} else ckmin(low[u],dfn[v]);
}
}
signed main() {
For(i,1,n) if( !dfn[i] ) tj(i,i);
}
- 圆方树
欧拉图
-
有向图是欧拉图
非零度结点弱连通,入度和出度相等 -
有向图是半欧拉图
非零度结点弱连通,恰有两个结点分别满足出度比入度大一、入度比出度大一
// 结点字典序最小的欧拉路径
int n,m,in[N],out[N];
Vi ans;
vector<Pii> e[N];
void adde(int id,int x,int y) { e[x].pb(y,id), ++out[x], ++in[y]; }
void dfs(int u) {
while( sz(e[u]) ) {
auto [v,id] = e[u].back(); e[u].pop_back();
dfs(v), ans.pb(id);
}
}
void MAIN() {
For(i,1,n) sort(all(e[i]),greater<>());
int s = 0;
For(i,1,n) if( out[i] > in[i] ) { s = i; break; }
if( !s ) For(i,1,n) if( out[i] ) { s = i; break; }
dfs(s);
if( sz(ans) != m ) ;
else rFor(i,m-1,0) cout<<ans[i]<<" ";
}
-
无向图是欧拉图
非零度结点连通,度数都为偶数 -
无向图是半欧拉图
非零度结点连通,恰有两个奇度结点
二分图
- 匈牙利算法
int n,vis[N],match[N];
Vi e[N];
int dfs(int u,int t=0) {
for(int v : e[u]) if( vis[v] != vis[0] ) {
vis[v] = vis[0];
if( match[v] == t || (match[v] > t && dfs(match[v],t)) )
return match[v] = u, v;
}
return 0;
}
signed main() {
For(i,1,n) sort(all(e[i]));
For(i,1,n) if(++vis[0]; !dfs(i) ) puts("NIE"), exit(0);
puts("TAK");
For(i,1,n) ++vis[0], write(dfs(i,i)); // 最小字典序
}
-
KM 算法
-
网络流
Dinic
网络流
- 最大流/最小割
Dinic
// 流量 long long
namespace flow {
const int N = ;
int S,T,fn,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(int n=0) { S = n+1, T = fn = n+2; }
void addedge(int x,int y,LL z=1e18) { 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];
mem(head,0,fn), mem(dis,0,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 in) {
if( u == T ) return in;
LL out = 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 f = dfs(v,min(c,in-out)) ) {
c -= f, e[v][b].c += f;
if( (out+=f) == in ) return in;
} else dis[v] = 0;
}
return out;
}
LL dinic() { LL mf = 0; while( bfs() ) mf += dfs(S,1e18); return mf; }
} using flow::fn; using flow::S; using flow::T; using flow::addedge;
- 最小费用最大流
SPFA+Dinic
// 流量 int,费用 long long
namespace flow {
const int N = ; const LL infl = 0x3f3f3f3f3f3f3f3f;
int S,T,fn,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(int n=0) { S = n+1, T = fn = n+2; }
void addedge(int x,int y,int z=1e9,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,1e9); mf += f, mc += f * dis[T]; } }
} using flow::fn; using flow::S; using flow::T; using flow::addedge;
- 最大费用任意流
增广至最长路为负
- 正费用最大流
总费用即将为负时停止流(注意最后一次增广可能无法获得增广路中全部流量)
- 最大权闭合子图
原图中边流量为
最小割中与
上下界
- 无源汇上下界可行流
以
记
:流量平衡,不需要调整 :相当于 在新图中凭空有 流量,连边 :类似的,连边
原图中流量平衡等价于新图中附加边流满,若新图最大流
- 有源汇上下界可行流
原图的源汇为
原图只有
- 有源汇上下界最大流
先求出有源汇上下界可行流,此时已满足流量平衡,然后删去附加边,在残量网络上求
实现上求可行流后附加边仅有
// loj116 有源汇有上下界最大流
read(n,m,s,t);
flow::init(n);
For(i,1,m, u,v,l,r)
read(u,v,l,r),
dlt[u] -= l, dlt[v] += l, addedge(u,v,r-l);
For(i,1,n)
if( dlt[i] > 0 ) addedge(S,i,dlt[i]), sum += dlt[i];
else if( dlt[i] < 0 ) addedge(i,T,-dlt[i]);
addedge(t,s,inf);
if( flow::isap() != sum ) puts("please go home to sleep"), exit(0);
S = s, T = t;
write(flow::isap());
- 有源汇上下界最小流
类似的,只需把可行流中不必要的流量退掉。在残量网络上求
实现上求可行流后需记录可行流大小,手动删除
// loj117 有源汇有上下界最小流
cin>>n>>m>>s>>t;
flow::init(n);
For(i,1,m, u,v,l,r)
cin>>u>>v>>l>>r,
dlt[u] -= l, dlt[v] += l, addedge(u,v,r-l);
For(i,1,n)
if( dlt[i] > 0 ) addedge(S,i,dlt[i]), sum += dlt[i];
else if( dlt[i] < 0 ) addedge(i,T,-dlt[i]);
addedge(t,s,1e18);
if( flow::dinic() != sum ) { cout<<"please go home to sleep\n"; return; }
LL ans = flow::e[s].back().c;
flow::e[s].pop_back(), flow::e[t].pop_back(), S = t, T = s;
cout<<ans-flow::dinic();
- 负圈最小费用最大流
有源汇上下界
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;
树
- Prufer 序列
// 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 amin(int u,int v) { return dfn[u]<dfn[v] ? u : v; }
void dfs(int u,int fa) {
dfn[u] = ++dfn[0], st[0][dfn[0]] = 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 amin(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] = amin(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();
}
}
}
分治
- 动态点分树
// LG3920 [WC2014] 紫荆花之恋
const int N = 1e5+5;
int n,val[N],dep[N],anc[N][47],dis[N][47],rt,siz[N],mxsz[N];
bool del[N];
Vi sub[N];
vector<Pii> e[N];
LL ans;
struct {
int dlt;
tree<Pii,null_type,less<>,rb_tree_tag,tree_order_statistics_node_update> t[N*2];
void clr(int u) { t[u].clear(); }
void ins(int u,int x) { t[u].insert({x,++dlt}); }
int rk(int u,int x) { return t[u].order_of_key({x+1,0}); }
} bst;
#define v ei.fi
#define w ei.se
void getrt(int u,int fa) {
siz[u] = mxsz[u] = 1;
for(auto &ei : e[u]) if( !del[v] && v != fa )
getrt(v,u), siz[u] += siz[v], ckmax(mxsz[u],siz[v]);
ckmax(mxsz[u],siz[0]-siz[u]);
if( mxsz[u] < mxsz[rt] ) rt = u;
}
void dfs(int u,int fa,int d) {
sub[rt].pb(u), anc[u][++dep[u]] = rt, dis[u][dep[u]] = d;
for(auto &ei : e[u]) if( !del[v] && v != fa ) dfs(v,u,d+w);
}
void dvd(int u,int size) {
siz[rt=0] = size, getrt(u,0), del[u=rt] = 1;
dfs(u,0,0);
for(auto &ei : e[u]) if( !del[v] ) dvd(v,size-mxsz[v]);
}
#undef v
#undef w
void rbld(int u) {
auto ver = sub[u]; int fa = anc[u][dep[u]-1];
for(int i : ver) {
while( anc[i][dep[i]] != fa ) --dep[i];
del[i] = 0, sub[i].clear(), bst.clr(i), bst.clr(n+i);
}
mxsz[0] = sz(ver), dvd(u,mxsz[0]);
for(int i : ver) for(int j = dep[i]; anc[i][j] != fa; --j)
bst.ins(anc[i][j],dis[i][j]-val[i]), bst.ins(n+anc[i][j],dis[i][j-1]-val[i]);
}
signed main() {
read(), io>>n;
read(),read(), io>>val[1];
anc[1][++dep[1]] = 1, dis[1][dep[1]] = 0,
del[1] = 1, sub[1].pb(1), bst.ins(1,0-val[1]);
io<<ans<<endl;
For(u,2,n) {
int fa,w; io>>fa>>w>>val[u], fa ^= ans%1000000000;
e[fa].pb(u,w), e[u].pb(fa,w), dep[u] = dep[fa];
For(i,1,dep[u]) anc[u][i] = anc[fa][i], dis[u][i] = dis[fa][i]+w;
anc[u][++dep[u]] = u, dis[u][dep[u]] = 0,
del[u] = 1, sub[u].pb(u), bst.ins(u,0-val[u]);
rFor(i,dep[u]-1,1) {
int x = anc[u][i], y = anc[u][i+1], d = dis[u][i];
ans += bst.rk(x,val[u]-d) - bst.rk(n+y,val[u]-d),
sub[x].pb(u), bst.ins(x,d-val[u]), bst.ins(n+y,d-val[u]);
}
Rep(i,1,dep[u]) if( sz(sub[anc[u][i]])*0.8 < sz(sub[anc[u][i+1]]) )
{ rbld(anc[u][i]); break; }
io<<ans<<endl;
}
return 0;
}
- 边分树合并
// uoj400【CTSC2018】暴力写挂
const int N = 733335;
const LL infl = 0x3f3f3f3f3f3f3f3f;
int n;
LL ans=-infl;
struct Graph {
int mm=1,head[N],nxt[N*2],to[N*2],w[N*2];
void adde(int x,int y,int z) {
nxt[++mm] = head[x], head[x] = mm, to[mm] = y, w[mm] = z,
nxt[++mm] = head[y], head[y] = mm, to[mm] = x, w[mm] = z;
}
};
#define eFor(g,u,i,v) for(int i = g.head[u], v; v = g.to[i], i; i = g.nxt[i])
#define ls(u) t[u].ch[0]
#define rs(u) t[u].ch[1]
struct {
int ind,rt[N],lst[N];
struct Node {
int ch[2]; LL mx[2];
Node() { memset(mx,0xcf,sizeof mx); }
} t[N*22];
void ins(int i,bool op,LL x) {
int u = lst[i]; lst[i] = ++ind;
t[u].ch[op] = ind, t[u].mx[op] = x;
}
int mrg(int u,int v,LL cs) {
if( !u || !v ) return u | v;
ckmax(ans,max(t[u].mx[0]+t[v].mx[1],t[u].mx[1]+t[v].mx[0])+cs),
ckmax(t[u].mx[0],t[v].mx[0]), ckmax(t[u].mx[1],t[v].mx[1]);
return ls(u) = mrg(ls(u),ls(v),cs), rs(u) = mrg(rs(u),rs(v),cs), u;
}
} t;
namespace T1 {
int nn,rt,mxsz,siz[N];
LL dis[N];
Graph g0,g;
void bld(int u,int fa) {
int x = 0;
eFor(g0,u,i,v) if( v != fa ) {
if( !x ) g.adde(u,v,g0.w[i]), x = u;
else g.adde(x,++nn,0), g.adde(x=nn,v,g0.w[i]);
dis[v] = dis[u]+g0.w[i], bld(v,u);
}
}
void getrt(int u,int fa) {
siz[u] = 1;
eFor(g,u,i,v) if( v && v != fa ) {
getrt(v,u), siz[u] += siz[v];
if( ckmin(mxsz,max(siz[v],siz[0]-siz[v])) ) rt = i;
}
}
void dfs(int u,int fa,LL d,bool op) {
if( u <= n ) t.ins(u,op,d+dis[u]);
eFor(g,u,i,v) if( v && v != fa ) dfs(v,u,d+g.w[i],op);
}
void dvd(int u,int size) {
if( size == 1 ) return;
siz[0] = mxsz = size, getrt(u,0);
u = g.to[rt]; int v = g.to[rt^1]; g.to[rt] = g.to[rt^1] = 0;
// cerr<<u<<' '<<v<<endl;
dfs(u,0,0,0), dfs(v,0,g.w[rt],1);
dvd(v,size-siz[u]), dvd(u,siz[u]);
}
void main() {
nn = n, bld(1,0);
// cerr<<"3 deg:\n";
// For(u,1,nn) eFor(g,u,i,v) if( u < v ) cerr<<u<<' '<<v<<' '<<g.w[i]<<endl;
For(i,1,n) t.rt[i] = t.lst[i] = ++t.ind;
dvd(1,nn);
}
}
namespace T2 {
Graph g;
void dfs(int u,int fa,LL d) {
ckmax(ans,2*(T1::dis[u]-d));
eFor(g,u,i,v) if( v != fa )
dfs(v,u,d+g.w[i]),
t.rt[u] = t.mrg(t.rt[u],t.rt[v],-2*d);
}
}
signed main() {
io>>n;
Rep(i,1,n, x,y,z) io>>x>>y>>z, T1::g0.adde(x,y,z);
Rep(i,1,n, x,y,z) io>>x>>y>>z, T2::g.adde(x,y,z);
T1::main(), T2::dfs(1,0,0);
io<<ans/2;
cerr<<"t.ind = "<<t.ind<<endl;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为DeepSeek添加本地知识库
· 精选4款基于.NET开源、功能强大的通讯调试工具
· DeepSeek智能编程
· 大模型工具KTransformer的安装
· [计算机/硬件/GPU] 显卡