嘘~ 正在从服务器偷取页面 . . .

OI模板-图论


OI模板-图论

最短路算法

dijkstra

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

优先队列优化 \(O((n+m)\log m)\)

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
#include <queue>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e5+15)
#define M (int)(2e5+15)

int n,m,pos=1,d[N],vis[N],head[N];
struct Edge{int u,v,w,next;} e[M];
struct node{int u,dis;};
bool operator<(node a,node b){return a.dis>b.dis;}
priority_queue<node> q;
void addEdge(int u,int v,int w)
{
    e[++pos]={u,v,w,head[u]};
    head[u]=pos;
}
void dijkstra(int st)
{
    for(int i=1; i<=n; i++) vis[i]=0,d[i]=INF;
    d[st]=0; q.push({st,0});
    while(!q.empty())
    {
        int u=q.top().u; q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u]; i; i=e[i].next)
        {
            int v=e[i].v,w=e[i].w;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                q.push({v,d[v]});
            }
        }
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int st; cin >> n >> m >> st;
    for(int i=1,u,v,w; i<=m; i++)
    {
        cin >> u >> v >> w;
        addEdge(u,v,w);
    }
    dijkstra(st);
    for(int i=1; i<=n; i++)
        cout << (d[i]==INF?-1:d[i]) << " \n"[i==n];
    return 0;
}

SPFA

时间复杂度 \(O(nm)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(2e3+15)
int n,m;
int d[N],vis[N];
struct Edge
{
    int u,v,w,next;
}e[N];
int pos=1,head[N];
void addEdge(int u,int v,int w)
{
    e[++pos]={u,v,w,head[u]};
    head[u]=pos;
}
queue<int> q;
void spfa()
{
    memset(d,0x3f,(n+1)*sizeof(int));
    memset(vis,0,(n+1)*sizeof(int));
    d[1]=0;vis[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u]; i; i=e[i].next)
        {
            int v=e[i].v;
            if(d[v]>d[u]+e[i].w)
            {
                d[v]=d[u]+e[i].w;
                if(!vis[v])
                    q.push(v),vis[v]=1;
            }
        }
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin >> n >> m;
    for(int i=1,u,v,w; i<=m; i++)
    {
        cin >> u >> v >> w;
        addEdge(u,v,w);
    }
    spfa();
    for(int i=1; i<=n; i++)
        cout << (d[i]==INF?-1:d[i]) << " \n"[i==n];
    return 0;
}

分层图最短路

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <queue>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e4+5)
#define M (int)(5e4+5)
#define K (int)(11)
struct Edge
{
    int u,v,w,next;
}e[M*(K+1)*4];
int n,m,k,s,t,pos=1,head[N*(K+1)],d[N*(K+1)],vis[N*(K+1)];
struct node{int u,dis;};
bool operator<(node a,node b){return a.dis>b.dis;}
priority_queue<node> q;
void addEdge(int u,int v,int w)
{
    e[++pos]={u,v,w,head[u]};
    head[u]=pos;
}
void dijkstra(int st)
{
    memset(d,0x3f,sizeof(d));
    q.push({st,0}); d[st]=0;
    while(!q.empty())
    {
        int u=q.top().u; q.pop();
        if(vis[u])continue;
        vis[u]=1;
        for(int i=head[u]; i; i=e[i].next)
        {
            int v=e[i].v,w=e[i].w;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                q.push({v,d[v]});
            }
        }
    }

}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m >> k >> s >> t; ++s; ++t;
    for(int i=1,u,v,w; i<=m; i++)
    {
        cin >> u >> v >> w; ++u; ++v;
        addEdge(u,v,w);addEdge(v,u,w);
        for(int j=1; j<=k; j++)
        {
            addEdge(j*n+u,j*n+v,w);
            addEdge(j*n+v,j*n+u,w);
            addEdge((j-1)*n+u,j*n+v,0);
            addEdge((j-1)*n+v,j*n+u,0);
        }
    }
    for(int i=1; i<=k; i++)
        addEdge((i-1)*n+t,i*n+t,0);
    dijkstra(s);
    cout << d[k*n+t] << '\n';
    return 0;
}

最小生成树

都是无向图!!!!有向图的叫最小树形图!!!

P3366 【模板】最小生成树

kruskal

时间复杂度 \(O(m\log m)\)

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(5e3+15)
#define M (int)(2e5+15)

int n,m,pos,res,head[N],f[N];
struct Edge{int u,v,w;}e[M];
bool operator<(Edge a,Edge b){return a.w<b.w;}
void init(int n){for(int i=1; i<=n; i++) f[i]=i;}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void merge(int u,int v){f[find(u)]=find(v);}
int kruskal()
{
    sort(e+1,e+1+m); int res=0,cnt=0;
    for(int i=1; i<=m&&cnt<n-1; i++)
        if(find(e[i].u)!=find(e[i].v))
        {
            merge(e[i].u,e[i].v);
            res+=e[i].w; ++cnt;
        }
    return (cnt!=n-1)?-1:res;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m; init(n);
    for(int i=1,u,v,w; i<=m; i++)
    {
        cin >> u >> v >> w;
        e[i]={u,v,w};
    }
    int ans=kruskal();
    if(ans==-1)cout << "orz";
    else cout << ans;
    return 0;
}

Boruvka

时间复杂度 \(O(m\log n)\)

空间复杂度 \(O(m)\)

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(5e3+15)
#define M (int)(2e5+15)

int n,m,head[N],f[N],best[M],vis[M];
struct Edge{int u,v,w;}e[M];
void init(int n){for(int i=1; i<=n; i++) f[i]=i;}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void merge(int u,int v){f[find(u)]=find(v);}
bool cmp(int a,int b)
{
    if(!b) return 1;
    if(e[a].w==e[b].w) return a<b;
    return e[a].w<e[b].w;
}
int boruvka()
{
    int res=0,cnt=0,ok=1;
    while(ok)
    {
        for(int i=0; i<=m; i++) best[i]=0;
        for(int i=1,u,v; i<=m; i++)
        {
            if(vis[i]) continue;
            u=find(e[i].u),v=find(e[i].v);
            if(u==v) continue;
            if(cmp(i,best[u])) best[u]=i;
            if(cmp(i,best[v])) best[v]=i;
        }
        ok=0;
        for(int i=1; i<=n&&cnt<n-1; i++)
            if(best[i]&&!vis[best[i]])
            {
                vis[best[i]]=1; ++cnt; ok=1;
                res+=e[best[i]].w;
                merge(e[best[i]].u,e[best[i]].v);
            }
    }
    return (cnt!=n-1)?-1:res;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m; init(n);
    for(int i=1,u,v,w; i<=m; i++)
    {
        cin >> u >> v >> w;
        e[i]={u,v,w};
    }
    int ans=boruvka();
    if(ans==-1) cout << "orz";
    else cout << ans << '\n';
    return 0;
}

Prim

优先队列优化的时间复杂度 \(O(m\log n)\)

斐波那契堆优化的时间复杂度 \(O(n\log n)\)

下面的代码是无优化邻接矩阵版的,\(O(n^2)\)仅适用于稠密图 \(n\le 2000\) ,不然会挂(MLE或TLE)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(2e3+15)
int n,m;
int ans,d[N],vis[N],g[N][N];
void prim()
{
	memset(d,0x3f,(n+1)*sizeof(int));
	for(int i=1; i<n; i++)
	{
		int u=0;
		for(int j=1; j<=n; j++)
			if(!vis[j]&&(!u||d[j]<d[u])) u=j;
		vis[u]=1;
		for(int j=1; j<=n; j++)
			if(!vis[j])d[j]=min(d[j],g[u][j]);
	}
	for(int i=2; i<=n; i++)
		ans+=d[i];
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin >> n >> m;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			if(i!=j)g[i][j]=INF;
	for(int i=1,u,v,w; i<=m; i++)
	{
		cin >> u >> v >> w;
		g[v][u]=g[u][v]=min(g[u][v],w);
	}
	prim();
	cout << ans << endl;
	return 0;
}

LCT

比较离谱的解法之一

大常数

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() getchar()
#define pc(a) putchar(a)
#define N (int)(4e5+5)
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    if(k>9)write(k/10);
    pc(k%10+'0');
}
int n,m,idx,ans,cnt;
namespace LCT
{
    struct node
    {
        int ch[2],w,id,fa,sz,mx,tag;
    }t[N];
    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))
    void pushr(int x)
    {
        swap(t[x].ch[0],t[x].ch[1]);
        t[x].tag^=1;
    }
    void push_up(int x)
    {
        t[x].id=x;t[x].mx=t[x].w;
        if(t[t[x].ch[0]].mx>t[x].mx)
            t[x].mx=t[t[x].ch[0]].mx,t[x].id=t[t[x].ch[0]].id;
        if(t[t[x].ch[1]].mx>t[x].mx)
            t[x].mx=t[t[x].ch[1]].mx,t[x].id=t[t[x].ch[1]].id;
    }
    void push_down(int x)
    {
        if(t[x].tag)
        {
            if(t[x].ch[0])pushr(t[x].ch[0]);
            if(t[x].ch[1])pushr(t[x].ch[1]);
            t[x].tag=0;
        }
    }
    void push_all(int x)
    {
        if(!isroot(x))push_all(t[x].fa);
        push_down(x);
    }
    void rotate(int x)
    {
        int y=t[x].fa;
        int z=t[y].fa;
        int k=t[y].ch[1]==x;
        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;
        t[x].fa=z;
        t[y].ch[k]=t[x].ch[k^1];
        t[t[x].ch[k^1]].fa=y;
        t[x].ch[k^1]=y;
        t[y].fa=x;
        push_up(y);
        push_up(x);
    }
    void splay(int x)
    {
        push_all(x);
        while(!isroot(x))
        {
            int y=t[x].fa;
            int z=t[y].fa;
            if(!isroot(y))
            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);
            rotate(x);
        }
    }
    void access(int x)
    {
        for(int y=0; x; y=x,x=t[x].fa)
            splay(x),t[x].ch[1]=y,push_up(x);
    }
    void make_root(int x)
    {
        access(x);splay(x);
        pushr(x);
    }
    int find_root(int x)
    {
        access(x);splay(x);
        while(t[x].ch[0])push_down(x),x=t[x].ch[0];
        splay(x);
        return x;
    }
    void split(int x,int y)
    {
        make_root(x);
        access(y);splay(y);
    }
    void link(int x,int y)
    {
        make_root(x);
        if(find_root(y)!=x)t[x].fa=y;
    }
    int ck(int x,int y)
    {
        make_root(x);
        return find_root(y)!=x;
    }
    void cut(int x,int y)
    {
        make_root(x);
        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])
        {
            t[x].ch[1]=t[y].fa=0;
            push_up(x);
        }
    }
}
signed main()
{
    using namespace LCT;
    read(n);read(m);
    idx=n;
    for(int i=1,x,y,z; i<=m; i++)
    {
        read(x);read(y);read(z);
        t[++idx].w=z;
        if(x!=y&&ck(x,y))link(x,idx),link(idx,y),ans+=z,++cnt;
        else
        {
            split(x,y);int now=t[y].id;
            if(t[now].mx<=z)continue;
            ans-=t[now].mx-z;splay(now);
            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;
            link(x,idx);link(idx,y);
        }
    }
    if(cnt<n-1)puts("orz");
    else write(ans),pc('\n');
    return 0;
}

线段树优化建图

板子1

CF786B Legacy题解

\(n\) 个结点, \(q\) 次操作,问操作后从 \(s\) 出发的单源最短路

操作如下

  • 输入 1 u v w 表示 \(u\)\(v\) 连一条有向边
  • 输入 2 u l r w 表示 \(u\)\([l,r]\) 的每个结点连一条有向边
  • 输入 3 u l r w 表示 \([l,r]\) 的每个结点向 \(u\) 连一条有向边

对于 \(100\%\) 的数据,\(1\le n,q \le 10^5\)\(1\le w \le 10^9\)

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
#include <queue>
#include <bitset>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define DEBUG puts("-------------")
namespace FastIO
{
    #define gc() readchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
}using namespace FastIO;
#define N ((int)(1e5+15))

const int D=5e5+15; // 两棵线段树的分界点
int n,m,s,d[N*10+255],a[N*10+255]; // a是结点编号对应的线段树叶子结点的编号
bitset<N*10+255> vis;
struct node1{int v,w;}; // 边
struct show_l_r{int l,r;}tr[N<<2]; // 辅助树
vector<node1> g[N*10+255]; // 两棵线段树
struct node2{int u,dis;};
bool operator<(node2 a,node2 b){return a.dis>b.dis;}
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
void build(int l,int r,int at)
{
    tr[at].l=l; tr[at].r=r;
    if(l==r) return a[l]=at, void(0);
    int mid=(l+r)>>1;
    g[at].push_back({ls(at),0});
    g[at].push_back({rs(at),0});
    g[ls(at)+D].push_back({at+D,0});
    g[rs(at)+D].push_back({at+D,0});
    build(l,mid,ls(at)); build(mid+1,r,rs(at));
}
void link(int nl,int nr,int u,int w,int opt,int at)
{
    int l=tr[at].l,r=tr[at].r;
    if(nl<=l&&r<=nr)
    {
        if(opt) g[at+D].push_back({u,w});
        else g[u+D].push_back({at,w});
        return;
    }
    int mid=(l+r)>>1;
    if(nl<=mid) link(nl,nr,u,w,opt,ls(at));
    if(nr>mid) link(nl,nr,u,w,opt,rs(at));
    
}
void Dijkstra(int st)
{
    priority_queue<node2> q;
    memset(d,0x3f,sizeof(d));
    d[st]=0; vis=0; q.push({st,0});
    while(!q.empty())
    {
        int u=q.top().u; q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(auto i : g[u])
        {
            int v=i.v,w=i.w;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                q.push({v,d[v]});
            }
        }
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    read(n); read(m); read(s);
    build(1,n,1);
    for(int i=1,op,u,v,l,r,w; i<=m; i++)
    {
        read(op);
        if(op==1)
        {
            read(u); read(v); read(w);
            g[a[u]].push_back({a[v],w});
        }
        else
        {
            read(u); read(l); read(r); read(w);
            link(l,r,a[u],w,op&1,1); // op=2: u->[l,r] , op=3:[l,r]->u;
        }
    }
    for(int i=1; i<=n; i++)
    {
        g[a[i]].push_back({a[i]+D,0}),
        g[a[i]+D].push_back({a[i],0});
    }
    Dijkstra(a[s]+D);
    for(int i=1; i<=n; i++)
        write(d[a[i]]!=INF ? d[a[i]] : -1),pc(" \n"[i==n]);
    return 0;
}

板子2

P6348 [PA2011]Journeys

咕咕咕。。。


判负环

板子题 P3385 【模板】负环 ,这题是判断从结点 \(1\) 为起点的负环

SPFA

// 2023年03月11日 01:05:55
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
typedef pair<int,int> pii;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(2e3 + 15))

vector< pair<int,int> > vec[N];
int n,m,cnt[N],d[N]; char vis[N];
#define addEdge(u,v,w) vec[u].emplace_back(v,w)
void clear() { for(int i = 1; i <= n; i++) vec[i].clear(); }
bool spfa(int s) // 从 s 开始的负环
{
    memset(vis, 0, (n + 5) * sizeof(char));
    memset(cnt, 0, (n + 5) * sizeof(int));
    memset(d, 0x3f, (n + 5) * sizeof(int));
    queue<int> q; q.push(s); vis[s] = 1; d[s] = 0;
    for(int v,w; !q.empty(); )
    {
        int u = q.front(); q.pop(); vis[u] = 0;
        for(pii it : vec[u])
        {
            v = it.first, w = it.second;
            if(d[v] > d[u] + w)
            {
                d[v] = d[u] + w;
                if(!vis[v])
                {
                    //++cnt[v]; // 判法1:入队次数
                    cnt[v] = cnt[u] + 1; // 判法2:最短路长度
                    if(cnt[v] == n) return 1; // 在涉及虚点的题中可能会改成 n+1
                    q.push(v); vis[v] = 1;
                }
            }
        }
    }
    return 0;
}
void solve()
{
    cin >> n >> m; clear();
    for(int i = 1, u, v, w; i <= m; i++)
    {
        cin >> u >> v >> w;
        addEdge(u,v,w); if(!(w < 0)) addEdge(v,u,w);
    }
    cout << (spfa(1) ? "YES" : "NO") << '\n';
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int Q; cin >> Q; while(Q--) solve();
    return 0;
}

Bellman-Ford

不适用于判断 \(u\) 结点出发的负环,但是适合全局负环

故洛谷板子题会 WA11 ,因为板子题是求的1出发能到的负环

但是求图是否有负环是正确的

时间复杂度 \(O(nm)\) ,常数极小。

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
#include <bitset>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N ((int)(2e3+15))
#define M ((int)(1e4+15))

int n,m,pos=1,head[N],d[N];
struct Edge{int u,v,w,next;} e[M];
void addEdge(int u,int v,int w)
{
    e[++pos]={u,v,w,head[u]};
    head[u]=pos;
}
bool work()
{
    for(int i=1; i<=n; i++) d[i]=0;
    for(int k=1; k<n; k++)
        for(int i=2; i<=pos; i++)
        {
            int u=e[i].u,v=e[i].v,w=e[i].w;
            if(d[v] > d[u] + w) d[v]=d[u]+w;
        }
    for(int i=2; i<=pos; i++)
    {
        int u=e[i].u,v=e[i].v,w=e[i].w;
        if(d[v] > d[u] + w) return 1;
    }
    return 0;
}
void clear(){pos=1; for(int i=1; i<=n; i++) head[i]=0;}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int Q; cin >> Q;
    while(Q--)
    {
        clear(); cin >> n >> m;
        for(int i=1,u,v,w; i<=m; i++)
        {
            cin >> u >> v >> w;
            addEdge(u,v,w);
            if(w>=0) addEdge(v,u,w);
        }
        cout << (work()? "YES\n" : "NO\n");
    }
    return 0;
}

kosaraju算法

时间复杂度 \(O(n+m)\)

空间复杂度 \(O(m)\)

例题:P3387 【模板】缩点

有向图强连通分量缩点

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
#include <queue>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e5+15)
bool vis[N];
vector<int> g1[N],g2[N],vec[N];
int n,m,cnt,scnt,f[N],sum[N],in[N],val[N],scc[N],dfn[N];
void addEdge1(int u,int v)
{
    g1[u].push_back(v);
    g2[v].push_back(u);
}
void addEdge2(int u,int v)
{
    vec[u].push_back(v);
}
void dfs1(int u)
{
    vis[u]=1;
    for(int v : g1[u])
        if(!vis[v]) dfs1(v);
    dfn[++cnt]=u;
}
void dfs2(int u,int id)
{
    scc[u]=id; sum[id]+=val[u];
    for(int v : g2[u])
        if(!scc[v]) dfs2(v,id);
}
void kosaraju()
{
    for(int i=1; i<=n; i++)
        if(!vis[i]) dfs1(i);
    scnt=0;
    for(int i=n; i>=1; i--)
        if(!scc[dfn[i]])
        {
            ++scnt;
            dfs2(dfn[i],scnt);
        }
}
int topo()
{
    queue<int> q;
    for(int i=1; i<=scnt; i++)
        if(!in[i]) q.push(i),f[i]=sum[i];
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int v:vec[u])
        {
            f[v]=max(f[v],f[u]+sum[v]);
            if(!(--in[v])) q.push(v);
        }
    }
    return *max_element(f+1,f+1+scnt);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m;
    for(int i=1; i<=n; i++)
        cin >> val[i];
    for(int i=1,u,v; i<=m; i++)
    {
        cin >> u >> v;
        addEdge1(u,v);
    }
    kosaraju();
    for(int i=1,u,v; i<=n; i++)
        for(int j : g1[i])
        {
            u=scc[i],v=scc[j];
            if(u!=v) addEdge2(u,v),++in[v];
        }
    cout << topo() << '\n';
    return 0;
}

Tarjan算法 [连通性问题]

无向图

割边(桥)

时间复杂度 \(O(n)\)

空间复杂度 \(O(m)\)

例题:T103481 【模板】割边

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define M (int)(6e5+25)
#define N (int)(5e4+25)
struct Edge
{
    int u,v,next;
}e[M];
int n,m,ans;
int pos=2,head[N],dfn[N],low[N],dfncnt,cut[M];
void addEdge(int u,int v)
{
    e[pos]={u,v,head[u]};
    head[u]=pos++;
}
void tarjan(int u,int in_edge)
{
    dfn[u]=low[u]=++dfncnt;
    for(int i=head[u]; i; i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v,i);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u])
                cut[i]=cut[i^1]=1;
        }else if(i!=(in_edge^1))
            low[u]=min(low[u],dfn[v]);
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin >> n >> m;
    for(int i=1,u,v; i<=m; i++)
    {
        cin >> u >> v;
        addEdge(u,v);addEdge(v,u);
    }
    for(int i=1; i<=n; i++)
        if(!dfn[i])tarjan(i,0);
    for(int i=2; i<pos; i+=2)
        if(cut[i])++ans;
    cout << ans << endl;
    return 0;
}

割点(割顶)

区别于桥,这个不用管fa结点,但是注意rt要有两个子结点都是“割点性质“

时间复杂度 \(O(n)\)

空间复杂度 \(O(m)\)

例题:P3388 【模板】割点(割顶)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define N (int)(2e4+15)
#define M (int)(2e5+15)
struct node
{
	int u,v,next;
}e[M];
int n,m,ans;
int pos=2,head[N],dfncnt,dfn[N],low[N],cut[N];
void addEdge(int u,int v)
{
	e[pos]={u,v,head[u]};
	head[u]=pos++;
}
void tarjan(int u,int rt)
{
	dfn[u]=low[u]=++dfncnt;
	int cnt=0;
	for(int i=head[u]; i; i=e[i].next)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			tarjan(v,rt);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u])
			{
				++cnt;
				if(u!=rt||cnt>1)
					cut[u]=1;
			}
		}low[u]=min(low[u],dfn[v]);
	}
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin >> n >> m;
	for(int i=1,u,v; i<=m; i++)
	{
		cin >> u >> v;
		addEdge(u,v);addEdge(v,u);
	}
	for(int i=1; i<=n; i++)
		if(!dfn[i])tarjan(i,i);
	for(int i=1; i<=n; i++)
		if(cut[i])++ans;
	cout << ans << endl;
	for(int i=1; i<=n; i++)
		if(cut[i])cout << i << " \n"[!--ans];
	return 0;
}

还有另一种写法,如下

// 2022年10月16日 21:43:19
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(2e4+15))
#define M ((int)(2e5+15))

int n,m,res,pos=1,head[N],dfn[N],low[N],cut[N];
struct Edge { int u,v,next; } e[M];
void addEdge(int u,int v) { e[++pos] = {u,v,head[u]}; head[u] = pos; }
void Tarjan(int u,int f)
{
    static int tim = 0; int C = 0;
    dfn[u] = low[u] = ++tim;
    for(int i=head[u]; i; i=e[i].next)
    {
        int v = e[i].v;
        if(!dfn[v])
        {
            ++C; Tarjan(v,u); down(low[u], low[v]);
            if(f && dfn[u] <= low[v]) cut[u] = 1;
        }else if(v != f) down(low[u], dfn[v]);
    }
    if(!f && C > 1) cut[u] = 1;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m;
    for(int i=1,u,v; i<=m; i++)
    {
        cin >> u >> v;
        addEdge(u,v); addEdge(v,u);
    }
    for(int i=1; i<=n; i++) if(!dfn[i]) Tarjan(i,0);
    for(int i=1; i<=n; i++) if(cut[i]) ++res;
    cout << res << '\n';
    for(int i=1; i<=n; i++) if(cut[i]) cout << i << " \n"[!(--res)];
    return 0;
}

边双连通分量

时间复杂度 \(O(n)\)

空间复杂度 \(O(m)\)

upd20220806 有个板子题P8436 【模板】边双连通分量

例题:P2860 [USACO06JAN]Redundant Paths G

当然你也可以用强连通分量的板子来写,因为边双转一下一定是强连通分量。

例题的代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() getchar()
#define pc(a) putchar(a)
#define N (int)(5e3+15)
#define M (int)(2e4+15)
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    if(k>9)write(k/10);
    pc(k%10+'0');
}
struct Edge
{
    int u,v,next;
}e[M];
int n,m,ans,ecc[N],top;
int pos=2,head[N],stk[N];
int dfn[N],low[N],dfncnt,ecnt,in[N];
void addEdge(int u,int v)
{
    e[pos]={u,v,head[u]};
    head[u]=pos++;
}
void tarjan(int u,int in_edge)
{
    dfn[u]=low[u]=++dfncnt;
    stk[++top]=u;
    for(int i=head[u]; i; i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v,i);
            low[u]=min(low[u],low[v]);
        }else if(i!=(in_edge^1))
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        ecc[u]=++ecnt;
        while(stk[top]!=u)
            ecc[stk[top--]]=ecnt;
        --top;
    }
}
signed main()
{
    read(n);read(m);
    for(int i=1,u,v; i<=m; i++)
    {
        read(u);read(v);
        addEdge(u,v);addEdge(v,u);
    }
    for(int i=1; i<=n; i++)
        if(!dfn[i])top=0,tarjan(i,0);
    for(int i=2; i<pos; i++)
        if(ecc[e[i].u]!=ecc[e[i].v])
            ++in[ecc[e[i].u]],++in[ecc[e[i].v]];
    int ans=0;
    for(int i=1; i<=ecnt; i++)
        if(in[i]==2)++ans;
    // 对于所有双连通分量,其缩点(e-dcc)后
    // 连(叶结点数+1)/2(即叶结点数除以2向上取整)条边
    // 可以使整个图变成双连通图
    write((ans+1)>>1);pc('\n');
    return 0;
}

点双连通分量

时间复杂度 \(O(n)\)

空间复杂度 \(O(m)\)

例题:T103492 【模板】点双连通分量

注:下面的第二种写法才可通过例题!

因为第一种把割点存在了第一个,和例题的标程不一样,然后例题没写special judge.

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() getchar()
#define pc(a) putchar(a)
#define N (int)(5e4+15)
#define M (int)(6e5+15)
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    if(k>9)write(k/10);
    pc(k%10+'0');
}
struct Edge
{
    int u,v,next;
}e[M];
int n,m,vcnt,cut[N],stk[N],top;
int pos=2,head[N],dfn[N],low[N],dfncnt;
vector<int> vcc[N];
void addEdge(int u,int v)
{
    e[pos]={u,v,head[u]};
    head[u]=pos++;
}
void tarjan(int u,int rt)
{
    dfn[u]=low[u]=++dfncnt;
    stk[++top]=u;int cnt=0;
    if(u==rt&&!head[u])
    {
        vcc[++vcnt].push_back(u);
        return;
    }
    for(int i=head[u]; i; i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v,rt);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                ++cnt;
                if(u!=rt||cnt>1)cut[u]=1;
                // 第一种写法,割点在vcc[vcnt][0]
                vcc[++vcnt].push_back(u);
                while(stk[top]!=v)
                    vcc[vcnt].push_back(stk[top--]);
                vcc[vcnt].push_back(stk[top--]);
                /* 第二种写法
                    int z;++vcnt;
                    do {
                        z=stk[top--];
                        vcc[vcnt].push_back(z);
                    } while (z!=v);
                    vcc[vcnt].push_back(u);
                */
            }
        }else low[u]=min(low[u],dfn[v]);
    }
}
signed main()
{
    read(n);read(m);
    for(int i=1,u,v; i<=m; i++)
    {
        read(u);read(v);
        if(u==v)continue;
        addEdge(u,v);addEdge(v,u);
    }
    for(int i=1; i<=n; i++)
        if(!dfn[i])top=0,tarjan(i,i);
    for(int i=1; i<=vcnt; i++)
        for(int j=0; j<vcc[i].size(); j++)
            write(vcc[i][j]),pc(" \n"[j+1==vcc[i].size()]);
    return 0;
}

有向图

强连通分量缩点

时间复杂度 \(O(n+m)\)

空间复杂度 \(O(n+m)\)

例题:P3387 【模板】缩点

Tarjan缩点后的点的排列顺序是逆拓扑序

// 2022年09月12日 00:28:14
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e4+15))
#define M ((int)(1e5+15))

int n,m,scnt,tim,stktop,pos=1,head[N],f[N];
int val[N],top[N],sum[N],scc[N],stk[N],ins[N],dfn[N],low[N];
struct Edge{int u,v,next; }e[M];
void addEdge(int u,int v) { e[++pos] = {u,v,head[u]}; head[u] = pos; }
void tarjan(int u)
{
    dfn[u] = low[u] = ++tim;
    stk[++stktop] = u; ins[u] = 1;
    for(int i=head[u]; i; i=e[i].next)
    {
        int v = e[i].v;
        if(!dfn[v]) { tarjan(v); down(low[u], low[v]); }
        else if(ins[v]) down(low[u], dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        top[++scnt] = u;
        for(int p=0; p!=u; )
        {
            p = stk[stktop--]; ins[p]=0;
            scc[p] = scnt; // ++sz[scnt];
            sum[scnt] += val[p];
        }
    }
}
int solve()
{
    for(int u=scnt; u; u--)
    {
        if(!f[u]) f[u] = sum[u];
        for(int i=head[u]; i; i=e[i].next)
        {
            int v=e[i].v;
            up(f[v], f[u] + sum[v]);
        }
    }
    return *max_element(f+1,f+1+scnt);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m;
    for(int i=1; i<=n; i++) cin >> val[i];
    for(int i=1,u,v; i<=m; i++) { cin >> u >> v; addEdge(u,v); }
    for(int i=1; i<=n; i++) if(!dfn[i]) tarjan(i);
    pos=1; for(int i=1; i<=n; i++) head[i] = 0;
    for(int i=2; i<=m+1; i++)
    {
        int u = scc[e[i].u], v = scc[e[i].v];
        if(u != v) addEdge(u,v);
    }
    cout << solve() << '\n';
    return 0;
}

欧拉路径

P7771 【模板】欧拉路径

求的是字典序最小的欧拉路径

/*
g++ test_20220124.cpp -o test_20220124
./test_20220124
*/
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define MAXN (int)(1e5+5)
#define gc() getchar()
#define pc(a) putchar(a)
template<typename T>void read(T &k)
{
	char ch=gc(); T x=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
	k=x*f;
}
template<typename T>void write(T k)
{
	if(k<0){k=-k;pc('-');}
	if(k>9)write(k/10);
	pc(k%10+'0');
}
int n,m,cnt,s,t;
struct node
{
	int v,id;
	bool operator<(node &o)
		{return v<o.v;}
};
vector<node> vec[MAXN];
int vis[MAXN<<1],in[MAXN],out[MAXN],top,stk[MAXN<<1],del[MAXN]; 
// del防极限数据 (1->2) * 1e5 && (2->1) * 1e5
void dfs(int u)
{
	for(int i=del[u]; i<vec[u].size(); i=max(i+1,del[u])/*可能被更新了*/)
	{
		if(!vis[vec[u][i].id])
		{
			vis[vec[u][i].id]=1;
			del[u]=i+1;
			dfs(vec[u][i].v);
		}
	}
	stk[++top]=u;
}
signed main()
{
	read(n);read(m);
	for(int i=1,u,v; i<=m; i++)
	{
		read(u);read(v);
		++out[u];++in[v];
		vec[u].push_back({v,i});
	}
	for(int i=1; i<=n; i++)
	{
		if(in[i]!=out[i])
		{
			++cnt;
			if(in[i]==out[i]-1)s=i;
			if(in[i]==out[i]+1)t=i;
		}
	}
	if(cnt!=0&&cnt!=2)return puts("No"),0;
	if(cnt==0)s=t=1;
	if(!s||!t)return puts("No"),0;
	for(int i=1; i<=n; i++)
		sort(vec[i].begin(),vec[i].end());
	dfs(s);
	while(top>0)
	{
		write(stk[top]);
		pc(" \n"[top==1]);--top;
	}
	return 0;
}

欧拉回路

hierholzer算法?待补


2-SAT

参考 2–SAT 学习笔记

// 2024年08月02日 18:08:11
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define pb push_back
#define N ((int)(2e6 + 15))

bool ins[N];
vector<int> vec[N];
int stktop, scnt, stk[N], low[N], dfn[N], scc[N], top[N]; 
void Tarjan(int u)
{
    static int tim = 0; dfn[u] = low[u] = ++tim;
    stk[++stktop] = u; ins[u] = true;
    for(int v : vec[u])
    {
        if(!dfn[v]) { Tarjan(v); down(low[u], low[v]); }
        else if(ins[v]) down(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        top[++scnt] = u;
        for(int p = 0; p != u; )
        { 
            p = stk[stktop--];
            ins[p] = false; scc[p] = scnt;
        }
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n, m; cin >> n >> m;
    while(m--)
    {
        static int i, j; static bool a, b;
        cin >> i >> a >> j >> b;
        vec[i + n * a].pb(j + n * (!b));
        vec[j + n * b].pb(i + n * (!a));
    }
    rep(i, 1, n * 2) if(!dfn[i]) Tarjan(i);
    rep(i, 1, n) if(scc[i] == scc[i + n]) return cout << "IMPOSSIBLE\n", 0;
    cout << "POSSIBLE\n";
    rep(i, 1, n) cout << (scc[i] < scc[i + n]) << " \n"[i == n];
    return 0;
}

网络流算法

有向图网络最大流

这里默认s与t连通,实际上代码里写了判断

Dinic

时间复杂度 \(\mathcal{O}(n^2m)\)

空间复杂度 \(\mathcal{O}(n+m)\)

// 2022年10月28日 16:36:03
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(215))
#define M ((int)(5e3+15))

int n,m,s,t,pos=1,head[N],now[N],dep[N];
struct Edge { int u,v,w,next; } e[M * 2];
void addEdge(int u,int v,int w)
{ e[++pos] = {u,v,w,head[u]}; head[u] = pos; }
queue<int> q;
bool bfs()
{
    for(int i=1; i<=n; i++) dep[i] = INF;
    q.push(s); dep[s] = 1; now[s] = head[s];
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int i=head[u]; i; i=e[i].next)
        {
            int v = e[i].v;
            if(e[i].w > 0 && dep[v] == INF)
            {
                dep[v] = dep[u] + 1;
                now[v] = head[v]; q.push(v);
            }
        }
    }
    return dep[t] != INF;
}
int dfs(int u,int in)
{
    if(u == t) return in;
    int out = 0;
    for(int i=now[u]; i; i=e[i].next)
    {
        if(!in) break; int v = e[i].v; now[u] = i;
        if(e[i].w > 0 && dep[v] == dep[u] + 1)
        {
            int res = dfs(v, min(in, e[i].w));
            e[i].w -= res; e[i^1].w += res;
            in -= res; out += res;
        }
    }
    if(!out) dep[u] = INF;
    return out;
}
int Dinic()
{
    int res = 0;
    while(bfs()) res += dfs(s, INF);
    return res;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m >> s >> t;
    for(int i=1,u,v,w; i<=m; i++)
    {
        cin >> u >> v >> w;
        addEdge(u,v,w); addEdge(v,u,0);
    }
    cout << Dinic() << '\n';
    return 0;
}

ISAP

时间复杂度 \(O(n^2m)\)

空间复杂度 \(O(m)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e4+15)
#define M (int)(2e4+15)
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e5+5)
char buf1[SIZ];
char *p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    static T stk[66];T top=0;
    do{stk[top++]=k%10,k/=10;}while(k);
    while(top){pc(stk[--top]+'0');}
}
struct Edge
{
    int u,v,w,next;
}e[M];
int n,m,s,t,ans;
int pos=1,head[N],dep[N],now[N],gap[N];
void addEdge(int u,int v,int w)
{
    e[++pos]={u,v,w,head[u]};
    head[u]=pos;
}
queue<int> q;
bool bfs()
{
    memset(dep,0x3f,(n+1)*sizeof(int));
    q.push(t);dep[t]=1;gap[1]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u]; i; i=e[i].next)
        {
            int v=e[i].v;
            if(e[i^1].w>0&&dep[v]==INF)
            {
                dep[v]=dep[u]+1;
                gap[dep[v]]++;
                q.push(v);
            }
        }
    }
    return dep[s]!=INF;
}
int dfs(int u,int in)
{
    if(u==t)
        return in;
    int out=0;
    for(int i=now[u]; i; i=e[i].next)
    {
        int v=e[i].v;now[u]=i;
        if(e[i].w>0&&dep[u]==dep[v]+1)
        {
            int res=dfs(v,min(in,e[i].w));
            e[i].w-=res;
            e[i^1].w+=res;
            in-=res;
            out+=res;
        }
        if(!in)return out;
    }
    if(!--gap[dep[u]])dep[s]=n+1;
    gap[++dep[u]]++;
    return out;
}
void ISAP()
{
    bfs();
    while(dep[s]<=n)
    {
        memcpy(now,head,(n+1)*sizeof(int));
        ans+=dfs(s,INF);
    }
}
signed main()
{
    read(n);read(m);read(s);read(t);
    for(int i=1,u,v,w; i<=m; i++)
    {
        read(u);read(v);read(w);
        addEdge(u,v,w);addEdge(v,u,0);
    }
    ISAP();
    write(ans);pc('\n');
    return 0;
}

HLPP

n=5e3,m=5e4

时间复杂度 \(O(n^2\sqrt{m}\log n)\)

空间复杂度 \(O(m)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
// #define INF 0x3f3f3f3f
#define gc() readchar()
#define pc(a) putchar(a)
#define N (int)(2e3+15)
#define M (int)(3e5+15)
#define SIZ (int)(1e5+5)
char buf1[SIZ];
char *p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    static T stk[66];T top=0;
    do{stk[top++]=k%10,k/=10;}while(k);
    while(top){pc(stk[--top]+'0');}
}
struct Edge{int v,w,next;};
vector<Edge> vec[N];
int n,m,s,t,now;
int gap[N],h[N],val[N],vis[N];
struct cmp
{
    bool operator()(int a,int b)
        {return h[a]<h[b];}
};
priority_queue<int,vector<int>,cmp> q;
queue<int> b;
bool bfs()
{
    memset(h,0x3f,(n+1)*sizeof(int));
    b.push(t);h[t]=0;vis[t]=1;
    while(!b.empty())
    {
        int u=b.front();b.pop();
        vis[u]=0;
        for(int i=0; i<vec[u].size(); i++)
        {
            int v=vec[u][i].v,j=vec[u][i].next;
            if(vec[v][j].w>0&&h[v]>h[u]+1)
            {
                h[v]=h[u]+1;
                if(!vis[v])
                    b.push(v),vis[v]=1;
            }
        }
    }
    return h[s]!=INF;
}
void relabel(int u)
{
    h[u]=INF;
    for(int i=0; i<vec[u].size(); i++)
    {
        int v=vec[u][i].v;
        if(vec[u][i].w>0&&h[u]>h[v]+1)
            h[u]=h[v]+1;
    }
}
int HLPP()
{
    if(!bfs())return -1;
    h[s]=n;
    for(int i=1; i<=n; i++)
        if(h[i]!=INF)++gap[h[i]];
    for(int i=0; i<vec[s].size(); i++)
    {
        int v=vec[s][i].v,w=vec[s][i].w,j=vec[s][i].next;
        if(w>0)
        {
            val[s]-=w;
            val[v]+=w;
            vec[s][i].w-=w;
            vec[v][j].w+=w;
            if(!vis[v]&&v!=t&&v!=s)
                q.push(v),vis[v]=1;
        }
    }
    while(!q.empty())
    {
        int u=q.top();
        q.pop();vis[u]=0;
        if(h[u]==INF)continue; // !!!!!!!!!!!
        for(int i=0; i<vec[u].size(); i++)
        {
            int v=vec[u][i].v,j=vec[u][i].next;
            if(vec[u][i].w>0&&h[u]==h[v]+1)
            {
                int w=min(vec[u][i].w,val[u]);
                val[u]-=w;
                val[v]+=w;
                vec[u][i].w-=w;
                vec[v][j].w+=w;
                if(!vis[v]&&v!=t&&v!=s)
                    q.push(v),vis[v]=1;
                if(!val[u])break;
            }
        }
        if(!val[u])continue;
        if(!--gap[h[u]])
        {
            for(int i=1; i<=n; i++)
                if(i!=s&&i!=t&&h[u]<h[i]&&h[i]<=n)
                    h[i]=n+1;
        }
        relabel(u);++gap[h[u]];
        q.push(u);vis[u]=1;
    }
    return val[t];
}
signed main()
{
    read(n);read(m);read(s);read(t);
    for(int i=1,u,v,w; i<=m; i++)
    {
        read(u);read(v);read(w);
        vec[u].push_back({v,w,(int)vec[v].size()});
        vec[v].push_back({u,0,(int)vec[u].size()-1});
    }
    write(HLPP());pc('\n');
    return 0;
}

有向图最小费用最大流

时间复杂度 \(O(nmf)\) ,其中 \(f\) 为最大流,一般不会被卡。

空间复杂度 \(O(m)\)

  1. vector写法,C++14 1.41s(O2 542ms)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() readchar()
#define pc(a) putchar(a)
#define N (int)(5e3+10)
#define M (int)(1e6+25)
#define SIZ (int)(1e5+5)
char buf1[SIZ];
char *p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    if(k>9)write(k/10);
    pc(k%10+'0');
}
struct Edge{int v,w,c,next;};
struct node{int u,i,j;}pre[N];
vector<Edge> vec[N];
int n,m,s,t,flow,cost;
int in[N],d[N],vis[N];
queue<int> q;
bool spfa()
{
    memset(d,0x3f,(n+1)*sizeof(int));
    memset(in,0x3f,(n+1)*sizeof(int));
    memset(vis,0,(n+1)*sizeof(int));
    q.push(s);vis[s]=1;d[s]=0;pre[t].u=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=0; i<vec[u].size(); i++)
        {
            int v=vec[u][i].v,w=vec[u][i].w;
            int c=vec[u][i].c,j=vec[u][i].next;
            if(w>0&&d[v]>d[u]+c)
            {
                d[v]=d[u]+c;
                pre[v]={u,i,j};
                in[v]=min(in[u],w);
                if(!vis[v])
                    q.push(v),vis[v]=1;
            }
        }
    }
    return pre[t].u!=0;
}
void Dinic()
{
    while(spfa())
    {
        flow+=in[t];
        cost+=in[t]*d[t];
        int u,i,j,v=t;
        while(v!=s)
        {
            u=pre[v].u,i=pre[v].i,j=pre[v].j;
            vec[u][i].w-=in[t];
            vec[v][j].w+=in[t];
            v=pre[v].u;
        }
    }
}
signed main()
{
    read(n);read(m);read(s);read(t);
    for(int i=1,u,v,w,c; i<=m; i++)
    {
        read(u);read(v);read(w);read(c);
        vec[u].push_back({v,w,c,(int)vec[v].size()});
        vec[v].push_back({u,0,-c,(int)vec[u].size()-1});
    }
    Dinic();
    write(flow);pc(' ');write(cost);pc('\n');
    return 0;
}
  1. 链式前向星写法,C++14 1.95s(O2 1.45s)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() readchar()
#define pc(a) putchar(a)
#define N (int)(5e3+10)
#define M (int)(1e6+25)
#define SIZ (int)(1e5+5)
char buf1[SIZ];
char *p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    if(k>9)write(k/10);
    pc(k%10+'0');
}
struct Edge
{
    int u,v,w,c,next;
}e[M];
int n,m,s,t,flow,cost;
int pos=1,head[N],in[N],d[N],pre[N],last[N],vis[N];
void addEdge(int u,int v,int w,int c)
{
    e[++pos]={u,v,w,c,head[u]};
    head[u]=pos;
}
struct node
{
    int u,dis;
    bool operator<(const node &o)const
        {return dis>o.dis;}
};
queue<int> q;
bool spfa()
{
    memset(d,0x3f,(n+1)*sizeof(int));
    memset(in,0x3f,(n+1)*sizeof(int));
    memset(vis,0,(n+1)*sizeof(int));
    q.push(s);vis[s]=1;d[s]=0;pre[t]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u]; i; i=e[i].next)
        {
            int v=e[i].v;
            if(e[i].w>0&&d[v]>d[u]+e[i].c)
            {
                d[v]=d[u]+e[i].c;
                pre[v]=u;
                last[v]=i;
                in[v]=min(in[u],e[i].w);
                if(!vis[v])
                    q.push(v),vis[v]=1;
            }
        }
    }
    return pre[t]!=0;
}
void Dinic()
{
    while(spfa())
    {
        flow+=in[t];
        cost+=in[t]*d[t];
        int at=t;
        while(at!=s)
        {
            e[last[at]].w-=in[t];
            e[last[at]^1].w+=in[t];
            at=pre[at];
        }
    }
}
signed main()
{
    read(n);read(m);read(s);read(t);
    for(int i=1,u,v,w,c; i<=m; i++)
    {
        read(u);read(v);read(w);read(c);
        addEdge(u,v,w,c);addEdge(v,u,0,-c);
    }
    Dinic();
    write(flow);pc(' ');write(cost);pc('\n');
    return 0;
}

有向图有源汇上下界最大流

例题:P5192 Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流

本例题建模:

  1. 先建立一个源点
  2. 从源点到每个少女,流量为 \(\left[G_i,+\infty\right)\)
  3. 从每个少女到每一天,流量为 \([l_i,r_i]\)
  4. 从每一天到汇点,流量为 \([0,D_i]\)

然后跑有源汇上下界最大流就好了

  1. ISAP版 C++14 O2 189ms
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
// #define gc() readchar()
#define gc() getchar()
#define pc(a) putchar(a)
#define N (int)(5e4+15)
#define M (int)(4e5+15)
#define SIZ (int)(1e5+15)
// char buf1[SIZ],*p1=buf1,*p2=buf1;
// char readchar()
// {
//     if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
//     return p1==p2?EOF:*p1++;
// }
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    static T stk[66];T top=0;
    do{stk[top++]=k%10,k/=10;}while(k);
    while(top){pc(stk[--top]+'0');}
}
struct Edge
{
    int u,v,w,next;
}e[M];
int n,m,s,t,s0,t0;
int pos=1,head[N],dep[N],now[N],gap[N],A[N];
void add(int u,int v,int w)
{
    e[++pos]={u,v,w,head[u]};head[u]=pos;
    e[++pos]={v,u,0,head[v]};head[v]=pos;
}
void addEdge(int u,int v,int l,int r)
{
    add(u,v,r-l);
    A[u]-=l;A[v]+=l;
}
queue<int> q;
bool bfs()
{
    memset(dep,0x3f,(n+m+5)*sizeof(int));
    memset(gap,0,(n+m+5)*sizeof(int));
    q.push(t0);dep[t0]=1;gap[1]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u]; i; i=e[i].next)
        {
            int v=e[i].v;
            if(e[i^1].w&&dep[v]==INF)
            {
                dep[v]=dep[u]+1;
                gap[dep[v]]++;
                q.push(v);
            }
        }
    }
    return dep[s0]!=INF;
}
int dfs(int u,int in)
{
    if(u==t0)return in;
    int out=0;
    for(int i=now[u]; i&&in; i=e[i].next)
    {
        int v=e[i].v;now[u]=i;
        if(e[i].w&&dep[u]==dep[v]+1)
        {
            int res=dfs(v,min(in,e[i].w));
            e[i].w-=res;
            e[i^1].w+=res;
            in-=res;
            out+=res;
        }
        if(!in)return out;
    }
    if(!--gap[dep[u]])dep[s0]=n+m+5;
    gap[++dep[u]]++;
    return out;
}
int ISAP()
{
    if(!bfs())return 0;
    int res=0;
    while(dep[s0]<=n+m+4)
    {
        memcpy(now,head,sizeof(head));
        res+=dfs(s0,INF);
    }
    return res;
}
signed main()
{
    while(~scanf("%lld%lld",&n,&m))
    {
        memset(head,0,(n+m+5)*sizeof(int));
        memset(A,0,(n+m+5)*sizeof(int));
        s=0;t=n+m+1;pos=1;
        int tot=0;
        for(int i=1; i<=m; i++)
        {
            int x;read(x);
            addEdge(s,i,x,INF);
        }
        for(int i=1,C,D; i<=n; i++)
        {
            read(C);read(D);
            addEdge(m+i,t,0,D);
            for(int j=1,x,L,R; j<=C; j++)
            {
                read(x);read(L);read(R);
                addEdge(x+1,m+i,L,R);
            }
        }
        s0=n+m+2;t0=n+m+3;
        for(int i=0; i<=n+m+1; i++)
        {
            if(A[i]>0)add(s0,i,A[i]),tot+=A[i];
            else if(A[i]<0)add(i,t0,-A[i]);
        }
        add(t,s,INF);
        if(ISAP()<tot){puts("-1\n");continue;}
        int res=e[pos].w;
        e[pos].w=e[pos-1].w=0;
        s0=s;t0=t;
        write(res+ISAP());puts("\n");
    }
    return 0;
}

无向图无源汇最大流

Stoer-Wagner

斯托瓦格纳?

  1. 朴素写法

    时间复杂度 \(O(nm+n^3)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e5+15)
char buf1[SIZ],*p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    static T stk[66];T top=0;
    do{stk[top++]=k%10,k/=10;}while(k);
    while(top){pc(stk[--top]+'0');}
}
#define N (int)(666)
int n,m,d[N][N],s,t,dap[N],vis[N],w[N],ord[N];
int proc(int x)
{
    memset(vis,0,(n+1)*sizeof(int));
    memset(w,0,(n+1)*sizeof(int));
    w[0]=-1;
    for(int i=1; i<=n-x+1; i++)
    {
        int mx=0;
        for(int j=1; j<=n; j++)
            if(!dap[j]&&!vis[j]&&w[j]>w[mx])mx=j;
        vis[mx]=1;ord[i]=mx;
        for(int j=1; j<=n; j++)
            if(!dap[j]&&!vis[j])w[j]+=d[mx][j];
    }
    s=ord[n-x];t=ord[n-x+1];
    return w[t];
}
int sw()
{
    int res=INF;
    for(int i=1; i<n; i++)
    {
        res=min(res,proc(i));
        dap[t]=1;
        for(int j=1; j<=n; j++)
        {
            d[s][j]+=d[t][j];
            d[j][s]+=d[j][t];
        }
    }
    return res;
}
signed main()
{
    read(n);read(m);
    for(int i=1,u,v,w; i<=m; i++)
    {
        read(u);read(v);read(w);
        d[u][v]+=w;d[v][u]+=w;
    }
    write(sw());pc('\n');
    return 0;
}
  1. 堆优化(意义不大)

    时间复杂度 \(O(nm+n^2\log n)\)

    还没写


最小树形图

最小树形图 朱刘算法

时间复杂度 \(O(nm)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e5+15)
char buf1[SIZ],*p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    static T stk[66];T top=0;
    do{stk[top++]=k%10,k/=10;}while(k);
    while(top){pc(stk[--top]+'0');}
}
#define M (int)(1e4+15)
#define N (int)(1e3+5)
struct Edge
{
    int u,v,w,next;
}e[M];
int n,m,rt;
int pre[N],ine[N];
int vis[N],id[N];
int pos=1,head[N];
void addEdge(int u,int v,int w)
{
    e[++pos]={u,v,w,head[u]};
    head[u]=pos;
}
int solve()
{
    int ans=0;
    while(1)
    {
        for(int i=1; i<=n; i++)
            ine[i]=INF;
        for(int i=2; i<=pos; i++)
        {
            int u=e[i].u,v=e[i].v;
            if(u!=v&&e[i].w<ine[v])
                ine[v]=e[i].w,pre[v]=u;
        }
        for(int i=1; i<=n; i++)
            if(i!=rt&&ine[i]==INF)return INF;
        int cnt=0;
        for(int i=1; i<=n; i++)
            vis[i]=id[i]=0;
        for(int i=1; i<=n; i++)
        {
            if(i==rt)continue;
            ans+=ine[i];
            int v=i;
            while(vis[v]!=i&&!id[v]&&v!=rt)
            {
                vis[v]=i;
                v=pre[v];
            }
            if(!id[v]&&v!=rt)
            {
                id[v]=++cnt;
                for(int u=pre[v]; u!=v; u=pre[u])
                    id[u]=cnt;
            }
        }
        if(!cnt)break;
        for(int i=1; i<=n; i++)
            if(!id[i])id[i]=++cnt;
        for(int i=2; i<=pos; i++)
        {
            int u=e[i].u,v=e[i].v;
            e[i].u=id[u];e[i].v=id[v];
            if(id[u]!=id[v])e[i].w-=ine[v];
        }
        rt=id[rt];
        n=cnt;
    }
    return ans;
}
signed main()
{
    read(n);read(m);read(rt);
    for(int i=1,u,v,w; i<=m; i++)
    {
        read(u);read(v);read(w);
        addEdge(u,v,w);
    }
    int res=solve();
    write(res==INF?-1:res);pc('\n');
    return 0;
}

最小树形图 Tarjan的DMST

时间复杂度 \(O(m+n\log m)\)

代码如下

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e5+15)
char buf1[SIZ],*p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    static T stk[66];T top=0;
    do{stk[top++]=k%10,k/=10;}while(k);
    while(top){pc(stk[--top]+'0');}
}
#define N (int)(2e3+15)
#define M (int)(2e4+15)
#define ls(x) t[x].ch[0]
#define rs(x) t[x].ch[1]
int n,m,r,cnt,f[N];
int vis[N],stk[N],top;
queue<int> q[N];
namespace leftist
{
    struct node
    {
        int ch[2],u,v,w,dist,tag;
    }t[M];
    int tot,rt[M];
    int New(int u,int v,int w)
    {
        t[++tot]={0,0,u,v,w};
        return tot;
    }
    void push_down(int x)
    {
        t[ls(x)].tag+=t[x].tag;
        t[ls(x)].w+=t[x].tag;
        t[rs(x)].tag+=t[x].tag;
        t[rs(x)].w+=t[x].tag;
        t[x].tag=0;
    }
    int merge(int x,int y)
    {
        if(!x||!y)return x|y;
        push_down(x);push_down(y);
        if(t[x].w>t[y].w)swap(x,y);
        rs(x)=merge(rs(x),y);
        if(t[rs(x)].dist>t[ls(x)].dist)
            swap(ls(x),rs(x));
        t[x].dist=t[rs(x)].dist+1;
        return x;
    }
    int remove(int x)
    {
        push_down(x);
        return merge(ls(x),rs(x));
    }
    void build()
    {
        for(int i=1; i<=n; i++)
        {
            if(q[i].empty())continue;
            while(q[i].size()!=1)
            {
                int p1=q[i].front();q[i].pop();
                int p2=q[i].front();q[i].pop();
                p1=merge(p1,p2);
                q[i].push(p1);
            }
            if(!q[i].empty())
            {
                rt[i]=merge(rt[i],q[i].front());
                q[i].pop();
            }
        }
    }
}
int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);}
signed main()
{
    using namespace leftist;
    read(n);read(m);read(r);
    for(int i=1,u,v,w; i<=m; i++)
    {
        read(u);read(v);read(w);
        int p=New(u,v,w);
        q[v].push(p);
        // rt[v]=merge(rt[v],p);
    }
    for(int i=1; i<=n; i++)
    {
        int p=New(i>1?i-1:n,i,INF);
        q[i].push(p);
        // rt[i]=merge(rt[i],p);
    }
    build();
    for(int i=1; i<=2*n; i++)f[i]=i;
    stk[++top]=r;vis[r]=1;
    int ans=0,cnt=n;
    while(rt[stk[top]])
    {
        int &p=rt[stk[top]];
        int u=find(t[p].u);
        if(u==stk[top])
        {
            p=remove(p);
            continue;
        }
        if(!vis[u])
        {
            stk[++top]=u;
            vis[u]=1;
            continue;
        }
        int q=++cnt;
        while(vis[u])
        {
            int v=stk[top--];
            vis[v]=0;f[v]=q;
            node *tmp=&t[rt[v]];
            tmp->tag-=tmp->w;
            if(find(tmp->v)!=find(r))
                ans+=tmp->w;
            rt[v]=remove(rt[v]);
            rt[q]=merge(rt[q],rt[v]);
        }
        stk[++top]=q;
        vis[q]=1;
    }
    ans=ans>=INF?-1:ans;
    write(ans);pc('\n');
    return 0;
}

改了一下范围是这样的

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e5+15)
char buf1[SIZ],*p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    static T stk[66];T top=0;
    do{stk[top++]=k%10,k/=10;}while(k);
    while(top){pc(stk[--top]+'0');}
}
#define N (int)(2e5+15)
#define M (int)(2e6+15)
#define ls(x) t[x].ch[0]
#define rs(x) t[x].ch[1]
int n,m,r,cnt,f[N];
int vis[N],stk[N],top;
queue<int> q[N];
namespace leftist
{
    struct node
    {
        int ch[2],u,v,w,dist,tag;
    }t[M];
    int tot,rt[M];
    int New(int u,int v,int w)
    {
        t[++tot]={0,0,u,v,w};
        return tot;
    }
    void push_down(int x)
    {
        t[ls(x)].tag+=t[x].tag;
        t[ls(x)].w+=t[x].tag;
        t[rs(x)].tag+=t[x].tag;
        t[rs(x)].w+=t[x].tag;
        t[x].tag=0;
    }
    int merge(int x,int y)
    {
        if(!x||!y)return x|y;
        push_down(x);push_down(y);
        if(t[x].w>t[y].w)swap(x,y);
        rs(x)=merge(rs(x),y);
        if(t[rs(x)].dist>t[ls(x)].dist)
            swap(ls(x),rs(x));
        t[x].dist=t[rs(x)].dist+1;
        return x;
    }
    int remove(int x)
    {
        push_down(x);
        return merge(ls(x),rs(x));
    }
    void build()
    {
        for(int i=1; i<=n; i++)
        {
            if(q[i].empty())continue;
            while(q[i].size()!=1)
            {
                int p1=q[i].front();q[i].pop();
                int p2=q[i].front();q[i].pop();
                p1=merge(p1,p2);
                q[i].push(p1);
            }
            if(!q[i].empty())
            {
                rt[i]=merge(rt[i],q[i].front());
                q[i].pop();
            }
        }
    }
}
int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);}
signed main()
{
    using namespace leftist;
    read(n);read(m);read(r);
    for(int i=1,u,v,w; i<=m; i++)
    {
        read(u);read(v);read(w);
        int p=New(u,v,w);
        q[v].push(p);
        // rt[v]=merge(rt[v],p);
    }
    for(int i=1; i<=n; i++)
    {
        int p=New(i>1?i-1:n,i,INF);
        q[i].push(p);
        // rt[i]=merge(rt[i],p);
    }
    build();
    for(int i=1; i<=2*n; i++)f[i]=i;
    stk[++top]=r;vis[r]=1;
    int ans=0,cnt=n;
    while(rt[stk[top]])
    {
        int &p=rt[stk[top]];
        int u=find(t[p].u);
        if(u==stk[top])
        {
            p=remove(p);
            continue;
        }
        if(!vis[u])
        {
            stk[++top]=u;
            vis[u]=1;
            continue;
        }
        int q=++cnt;
        while(vis[u])
        {
            int v=stk[top--];
            vis[v]=0;f[v]=q;
            node *tmp=&t[rt[v]];
            tmp->tag-=tmp->w;
            if(find(tmp->v)!=find(r))
                ans+=tmp->w;
            rt[v]=remove(rt[v]);
            rt[q]=merge(rt[q],rt[v]);
        }
        stk[++top]=q;
        vis[q]=1;
    }
    ans=ans>=INF?-1:ans;
    write(ans);pc('\n');
    return 0;
}

二分图

二分图最大匹配

匈牙利算法

时间复杂度 \(O(nm)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e3+5)
#define M (int)(1e5+15)
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e6+5)
char buf1[SIZ],*p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc(); T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    static T stk[66];T top=0;
    do{stk[top++]=k%10,k/=10;}while(k);
    while(top){pc(stk[--top]+'0');}
}
int n1,n2,m,vis[N],mch[N],ans;
vector<int> vec[N];
bool dfs(int u,int now)
{
    if(vis[u]==now)return 0;
    vis[u]=now;
    for(int v:vec[u])
        if(!mch[v]||dfs(mch[v],now))
        {
            mch[v]=u;
            return 1;
        }
    return 0;
}
signed main()
{
    read(n1);read(n2);read(m);
    for(int i=1,u,v; i<=m; i++)
    {
        read(u);read(v);
        vec[u].push_back(v);
    }
    for(int i=1; i<=n1; i++)
        ans+=dfs(i,i);
    write(ans);pc('\n');
    return 0;
}

ISAP

时间复杂度 \(O(m\sqrt{n})\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(2e3+5)
#define M (int)(5e4+15)
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ (int)(1e6+5)
char buf1[SIZ],*p1=buf1,*p2=buf1;
char readchar()
{
    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
    return p1==p2?EOF:*p1++;
}
template<typename T>void read(T &k)
{
    char ch=gc();T x=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
    k=x*f;
}
template<typename T>void write(T k)
{
    if(k<0){k=-k;pc('-');}
    static T stk[66];T top=0;
    do{stk[top++]=k%10,k/=10;}while(k);
    while(top){pc(stk[--top]+'0');}
}
struct Edge
{
    int u,v,w,next;
}e[M<<3];
int n1,n2,m,pos=1,ans,head[N],now[N],s,t,gap[N],dep[N];
void addEdge(int u,int v,int w)
{
    e[++pos]={u,v,w,head[u]};
    head[u]=pos;
}
queue<int> q;
void bfs()
{
    memset(dep,0x3f,sizeof(dep));
    q.push(t);dep[t]=1;gap[1]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u]; i; i=e[i].next)
        {
            int v=e[i].v;
            if(e[i^1].w>0&&dep[v]==INF)
            {
                dep[v]=dep[u]+1;
                gap[dep[v]]++;
                q.push(v);
            }
        }
    }
}
int dfs(int u,int in)
{
    if(u==t)return in;
    int out=0;
    for(int i=now[u]; i; i=e[i].next)
    {
        int v=e[i].v;now[u]=i;
        if(e[i].w>0&&dep[u]==dep[v]+1)
        {
            int res=dfs(v,min(in,e[i].w));
            e[i].w-=res;
            e[i^1].w+=res;
            in-=res;
            out+=res;
        }
        if(!in)return out;
    }
    if(!--gap[dep[u]])dep[s]=n1+n2+3;
    gap[++dep[u]]++;
    return out;
}
signed main()
{
    read(n1);read(n2);read(m);
    s=1;t=n1+n2+2;
    for(int i=1,u,v; i<=m; i++)
    {
        read(u);read(v);
        addEdge(u+1,v+n1+1,1);addEdge(v+n1+1,u+1,0);
    }
    for(int i=1; i<=n1; i++)
        addEdge(s,i+1,1),addEdge(i+1,s,0);
    for(int i=1; i<=n2; i++)
        addEdge(n1+i+1,t,1),addEdge(t,n1+i+1,0);
    
    bfs();
    while(dep[s]<=n1+n2+2)
        memcpy(now,head,sizeof(head)),
        ans+=dfs(s,INF);
    write(ans);pc('\n');
    return 0;
}

二分图最大权完美匹配

二分图最大权匹配可以用费用流来做,时间复杂度仍为 \(\mathcal{O}(nmf)\) ,代码还木有。

KM算法

时间复杂度 \(O(n^3)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(555)
int n,m;
int g[N][N],slack[N];
int lx[N],ly[N],px[N],py[N],pre[N],d;
bool vx[N],vy[N];
void aug(int v)
{
    int t;
    while(v)
    {
        t=px[pre[v]];
        px[pre[v]]=v;
        py[v]=pre[v];
        v=t;
    }
}
queue<int> q;
void bfs(int s)
{
    memset(vx,0,sizeof(vx));
    memset(vy,0,sizeof(vy));
    for(int i=1; i<=n; i++)
        slack[i]=INF;
    while(!q.empty())q.pop();
    q.push(s);
    while(1)
    {
        while(!q.empty())
        {
            int u=q.front();q.pop();
            vx[u]=1;
            for(int i=1; i<=n; i++)
            {
                if(!vy[i]&&lx[u]+ly[i]-g[u][i]<slack[i])
                {
                    slack[i]=lx[u]+ly[i]-g[u][i];
                    pre[i]=u;
                    if(!slack[i])
                    {
                        vy[i]=1;
                        if(!py[i]){aug(i);return;}
                        else q.push(py[i]);
                    }
                }
            }
        }
        int d=INF;
        for(int i=1; i<=n; i++)
            if(!vy[i])d=min(d,slack[i]);
        for(int i=1; i<=n; i++)
        {
            if(vx[i])lx[i]-=d;
            if(vy[i])ly[i]+=d;
            else slack[i]-=d;
        }
        for(int i=1; i<=n; i++)
        {
            if(!vy[i]&&!slack[i])
            {
                vy[i]=1;
                if(!py[i]){aug(i);return;}
                else q.push(py[i]);
            }
        }
    }
}
int KM()
{
    int res=0;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            lx[i]=max(lx[i],g[i][j]);
    for(int i=1; i<=n; i++)bfs(i);
    for(int i=1; i<=n; i++)
        res+=g[py[i]][i];
    return res;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin >> n >> m;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            g[i][j]=-INF;
    for(int i=1,u,v,w; i<=m; i++)
    {
        cin >> u >> v >> w;
        g[u][v]=max(g[u][v],w);
    }
    cout << KM() << endl;
    for(int i=1; i<=n; i++)
        cout << py[i] << " \n"[i==n];
    return 0;
}

一般图最大匹配

带花树算法

时间复杂度 \(O(n^3)\)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e3+15)
int n,m,cnt,vis[N];
int mch[N],pre[N],f[N],col[N];
vector<int> vec[N];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
queue<int> q;
void aug(int v)
{
    int t;
    while(v)
    {
        t=mch[pre[v]];
        mch[v]=pre[v];
        mch[pre[v]]=v;
        v=t;
    }
}
int lca(int u,int v)
{
    ++cnt;
    u=find(u);v=find(v);
    while(vis[u]!=cnt)
    {
        vis[u]=cnt;
        u=find(pre[mch[u]]);
        if(v)swap(u,v);
    }
    return u;
}
void shrink(int u,int v,int p)
{
    while(find(u)!=p)
    {
        pre[u]=v;
        v=mch[u];
        if(col[v]==2)
            col[v]=1,q.push(v);
        f[u]=p;f[v]=p;
        u=pre[v];
    }
}
bool bfs(int s)
{
    for(int i=1; i<=n; i++)
        f[i]=i,pre[i]=col[i]=0;
    while(!q.empty())q.pop();
    q.push(s);col[s]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int v:vec[u])
        {
            if(!col[v])
            {
                pre[v]=u;
                if(!mch[v]){aug(v);return 1;}
                else col[v]=2,col[mch[v]]=1,q.push(mch[v]);
            }else if(col[v]==1&&find(u)!=find(v))
            {
                int p=lca(u,v);
                shrink(u,v,p);shrink(v,u,p);
            }
        }
    }
    return 0;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin >> n >> m;
    for(int i=1,u,v; i<=m; i++)
    {
        cin >> u >> v;
        vec[u].push_back(v);
        vec[v].push_back(u);
    }
    int res=0;
    for(int i=1; i<=n; i++)
        res+=!mch[i]&&bfs(i);
    cout << res << endl;
    for(int i=1; i<=n; i++)
        cout << mch[i] << " \n"[i==n];
    return 0;
}

一般图最大权匹配

不会 qwq 感觉要等到我大学才会去学,咕咕咕...


无向图的最小环问题

下面这个代码求的是至少包含 \(3\) 个点的环

要根据题意判断

 #include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f
int n,m,ans=INF;
int g[105][105],f[105][105];
signed main()
{
    ios::sync_with_stdio(0);
    cin >> n >> m;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            g[i][j]=f[i][j]=INF;
    for(int i=1,u,v,w; i<=m; i++)
    {
        cin >> u >> v >> w;
        g[u][v]=min(g[u][v],w);
        g[v][u]=min(g[v][u],w);
        f[u][v]=min(f[u][v],w);
        f[v][u]=min(f[v][u],w);
    }
    for(int k=1; k<=n; k++)
    {
        for(int i=1; i<k; i++)
            for(int j=i+1; j<k; j++)
                ans=min(ans,f[i][j]+g[i][k]+g[k][j]);
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
    }
    if(ans==INF)cout << "No solution.";
    else cout << ans;
    return 0;
}

点分治

时间复杂度 \(\mathcal{O}(n \log n)\)

模板题:洛谷P3806 【模板】点分治 1

// 2024年05月26日 19:08:34
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e4 + 15))
#define M ((int)(115))
#define K ((int)(1e7 + 15))

int n, m, rt, rt_fa, sum, pos = 1, head[N], sz[N], mx[N], qry[N];
int tot, q[N], val[N], dis[N]; char ans[M], vis[N], pd[K];
struct Edge { int u, v, w, next; } e[N * 2];
void addEdge(int u, int v, int w) {
    e[++pos] = {u, v, w, head[u]}; head[u] = pos;  
}
void getroot(int u, int fa)
{
    mx[u] = 0; sz[u] = 1;
    for(int i = head[u]; i; i = e[i].next)
    {
        int v = e[i].v; if(v == fa || vis[v]) continue;
        getroot(v, u); sz[u] += sz[v]; up(mx[u], sz[v]);
    }
    up(mx[u], sum - sz[u]); if(mx[u] < mx[rt]) { rt = u, rt_fa = fa; }
}
void getdis(int u, int fa)
{
    if(dis[u] > 10000000) return; 
    val[++tot] = dis[u];
    for(int i = head[u]; i; i = e[i].next)
    {
        int v = e[i].v; if(v == fa || vis[v]) continue;
        dis[v] = dis[u] + e[i].w; getdis(v, u);
    }
}
void init(int u)
{
    int c = 0;
    for(int i = head[u]; i; i = e[i].next)
    {
        int v = e[i].v; if(vis[v]) continue;
        tot = 0; dis[v] = e[i].w; getdis(v, u);
        for(int j = 1; j <= tot; j++)
            for(int k = 1; k <= m; k++)
                if(qry[k] >= val[j]) ans[k] |= pd[qry[k] - val[j]];
        for(int j = 1; j <= tot; j++) { q[++c] = val[j], pd[val[j]] = true; }
    }
    for(int i = 1; i <= c; i++) pd[q[i]] = false;
}
void solve(int u)
{
    vis[u] = pd[0] = true; init(u);
    for(int i = head[u]; i; i = e[i].next)
    {
        int v = e[i].v; if(vis[v]) continue;
        sum = v == rt_fa ? n - sz[u] : sz[v];
        mx[rt = rt_fa = 0] = n; getroot(v, u); solve(rt);
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m; 
    for(int i = 1, u, v, w; i < n; i++)
    {
        cin >> u >> v >> w;
        addEdge(u, v, w); addEdge(v, u, w);
    }
    for(int i = 1; i <= m; i++) cin >> qry[i];
    mx[rt = rt_fa = 0] = sum = n;
    getroot(1, 0); solve(rt);
    for(int i = 1; i <= m; i++) cout << (ans[i] ? "AYE" : "NAY") << '\n';
    return 0;
}

参考文献

[1] https://www.cnblogs.com/SYCstudio/p/7260613.html

[2] https://www.xht37.com/%E4%BA%8C%E5%88%86%E5%9B%BE%E4%B8%8E%E7%BD%91%E7%BB%9C%E6%B5%81-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录