图论模板

用户头像 发布于 2022-11-21 最后更新于 2025-11-20 59 次阅读


AI 摘要

图论的奇妙世界,你是否已准备好探索?从链式前向星的精巧存储,到树的直径、LCA的深度解析,再到最小生成树、最短路的经典算法,以及强连通分量的精妙划分,本文将带你领略图论的魅力,解锁解决复杂问题的强大武器。

图的存储

链式前向星

class graph{
    int h[N];

    struct{
        int t,p,w;
    }e[M];

    public:

    void add_one(int x, int y, int w){
        static int c;
        e[++c]={y,h[x],w}, h[x]=c;
    }

    void add(int x, int y, int w){
        add_one(x,y,w), add_one(y,x,w);
    }

    void dfs(int x){
        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            /* 操作 */
        }
    }
};

树上问题

树的直径

两次 DFS

class graph{
    int mx,p,d[N],f[N];

    public:

    void dfs(int x, int fa){
        if(d[x]>mx) mx=d[x], p=x;

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(y!=fa) d[y]=d[x]+1, f[y]=x, dfs(y,x);
        }
    }

    int dia(){
        dfs(1,0), mx=d[p]=0, dfs(p,0);
        return d[p];
    }
};

树形 DP

int n;

class graph{
    public:

    void dfs(int x, int f){
        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(y==f) continue;

            dfs(y,x);
            if(dp[y][0]+e[i].w>dp[x][0]) dp[x][1]=dp[x][0], dp[x][0]=dp[y][0]+e[i].w;
            else dp[x][1]=max(dp[x][1],dp[y][0]+e[i].w);
        }
    }

    int dia(){
        dfs(1,0);

        int mx=0;
        for(int i=1;i<=n;i++) mx=max(mx,dp[i][0]+dp[i][1]);
        return mx;
    }
};

最近公共祖先

倍增算法

class graph{
    int d[N],f[N][L];

    public:

    void dfs(int x, int fa){
        d[x]=d[fa]+1, f[x][0]=fa;
        for(int i=1; 1<<i<=d[x]; i++) f[x][i]=f[f[x][i-1]][i-1];

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(y!=fa) dfs(y,x);
        }
    }

    int lca(int x, int y){
        if(d[x]<d[y]) swap(x,y);

        for(int i=log2(d[x]); ~i; i--){
            if(d[x]-d[y]>=1<<i) x=f[x][i];
        }
        if(x==y) return x;

        for(int i=log2(d[x]); ~i; i--){
            if(f[x][i]!=f[y][i]) x=f[x][i], y=f[y][i];
        }
        return f[x][0];
    }
};

树链剖分

class graph{
    int mx[N],f[N],d[N],t[N];

    void dfs1(int x, int fa){
        static int s[N];
        f[x]=fa, d[x]=d[fa]+1, s[x]=1;

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(y==fa) continue;

            dfs1(y,x), s[x]+=s[y];
            if(s[y]>s[mx[x]]) mx[x]=y;
        }
    }

    void dfs2(int x, int p){
        static int c,dfn[N];
        dfn[x]=++c, t[x]=p;
        if(!mx[x]) return;
        dfs2(mx[x],p);

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(y!=f[x] && y!=mx[x]) dfs2(y,y);
        }
    }

    public:

    void init(int x){
        dfs1(x,0), dfs2(x,0);
    }

    int lca(int x, int y){
        while(t[x]!=t[y]){
            if(d[t[x]]>d[t[y]]) x=f[t[x]];
            else y=f[t[y]];
        }
        return d[x]<d[y] ? x : y;
    }
};

树的重心

const int INF=0x3f3f3f3f;

int n,dp[N];

class graph{
    public:

    void dfs(int x, int f){
        static int s[N];
        s[x]=1;

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(y!=f) dfs(y,x), s[x]+=s[y], dp[x]=max(dp[x],s[y]);
        }
        dp[x]=max(dp[x],n-s[x]);
    }
};

树链剖分

int n,MOD,w0[N],w[N];
seg a;

class graph{
    int mx[N],f[N],d[N],s[N],t[N],dfn[N];

    void dfs1(int x, int fa){
        f[x]=fa, d[x]=d[fa]+1, s[x]=1;

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(y==fa) continue;

            dfs1(y,x), s[x]+=s[y];
            if(s[y]>s[mx[x]]) mx[x]=y;
        }
    }

    void dfs2(int x, int p){
        static int c;
        dfn[x]=++c, w[c]=w0[x], t[x]=p;
        if(!mx[x]) return;
        dfs2(mx[x],p);

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(y!=f[x] && y!=mx[x]) dfs2(y,y);
        }
    }

    public:

    void init(int x){
        dfs1(x,0), dfs2(x,0), a.build(1,1,n);
    }

    void update(int x, int y, long long w){
        while(t[x]!=t[y]){
            if(d[t[x]]<d[t[y]]) swap(x,y);
            a.update(1,dfn[t[x]],dfn[x],w), x=f[t[x]];
        }

        if(d[x]<d[y]) swap(x,y);
        a.update(1,dfn[y],dfn[x],w);
    }

    long long query(int x, int y){
        long long sum=0;
        while(t[x]!=t[y]){
            if(d[t[x]]<d[t[y]]) swap(x,y);
            sum=(sum+a.query(1,dfn[t[x]],dfn[x]))%MOD, x=f[t[x]];
        }

        if(d[x]<d[y]) swap(x,y);
        return (sum+a.query(1,dfn[y],dfn[x]))%MOD;
    }

    void update(int x, long long w){
        a.update(1,dfn[x],dfn[x]+s[x]-1,w);
    }

    long long query(int x){
        return a.query(1,dfn[x],dfn[x]+s[x]-1);
    }
};

换根

class graph{
    public:

    int r;

    void update(int x, long long w){
        if(dfn[x]+s[x]-1>=dfn[r] && dfn[x]<dfn[r]){
            a.update(1,1,n,w);

            int y=r;
            while(x!=f[y]) y=f[y];
            a.update(1,dfn[y],dfn[y]+s[y]-1,-w);
        }
        else if(x==r) a.update(1,1,n,w);
        else a.update(1,dfn[x],dfn[x]+s[x]-1,w);
    }

    long long query(int x){
        if(dfn[x]+s[x]-1>=dfn[r] && dfn[x]<dfn[r]){
            long long sum=a.query(1,1,n);

            int y=r;
            while(x!=f[y]) y=f[y];
            return sum-a.query(1,dfn[y],dfn[y]+s[y]-1);
        }
        else if(x==r) return a.query(1,1,n);
        else return a.query(1,dfn[x],dfn[x]+s[x]-1);
    }
};

最小生成树

int n,m;

struct node{
    int x,y,w;

    bool operator <(const node x) const{
        return w<x.w;
    }
}e[M];

mfs f;

int mst(){
    f.init(n), sort(e+1,e+m+1);

    int c=0, s=0;
    for(int i=1; i<=m; i++){
        if(!f.merge(e[i].x,e[i].y)) continue;

        s+=e[i].w;
        if(++c==n-1) return s;
    }
    return -1;
}

最短路

Floyd 算法

const int INF=0x3f3f3f3f;

int n,d[N][N];

void init(){
    memset(d,INF,sizeof d);
    for(int i=1; i<=n; i++) d[i][i]=0;
}

void dis(){
    for(int k=1; k<=n; k++){
        for(int i=1; i<=n; i++){
            for(int j=1; j<=n; j++) d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
        }
    }
}

Bellman-Ford 算法

队列优化:SPFA

const int INF=0x3f3f3f3f;

int d[N];

class graph{
    public:

    void dis(int s){
        bool u[N]={};
        queue<int> q;
        memset(d,INF,sizeof d), d[s]=0, q.push(s);

        do{
            int x=q.front();
            q.pop();
            u[x]=0;

            for(int i=h[x]; i; i=e[i].p){
                int y=e[i].t;
                if(d[x]+e[i].w<d[y]){
                    d[y]=d[x]+e[i].w;
                    if(!u[y]) u[y]=1, q.push(y);
                }
            }
        }while(!q.empty());
    }
};

负环

const int INF=0x3f3f3f3f;

class graph{
    public:

    bool dis(int s){
        int d[N]={},c[N]={};
        bool u[N]={};
        queue<int> q;
        memset(d,INF,sizeof d), d[s]=0, q.push(s);

        do{
            int x=q.front();
            q.pop();
            u[x]=0;

            for(int i=h[x]; i; i=e[i].p){
                int y=e[i].t;
                if(d[x]+e[i].w<d[y]){
                    d[y]=d[x]+e[i].w;
                    if((c[y]=c[x]+1)>=n) return 1;
                    if(!u[y]) u[y]=1, q.push(y);
                }
            }
        }while(!q.empty());

        return 0;
    }
};

Dijkstra 算法

typedef pair<int,int> node;
#define d first
#define p second

const int INF=0x3f3f3f3f;

int d[N];

class graph{
    public:

    void dis(int s){
        bool u[N]={};
        priority_queue<node,vector<node>,greater<node>> q;
        memset(d,INF,sizeof d), d[s]=0, q.push({0,s});

        do{
            int x=q.top().p;
            q.pop();
            if(u[x]) continue;
            u[x]=1;

            for(int i=h[x]; i; i=e[i].p){
                int y=e[i].t;
                if(!u[y] && d[x]+e[i].w<d[y]) q.push({d[y]=d[x]+e[i].w,y});
            }
        }while(!q.empty());
    }
};

连通性相关

强连通分量

int n,p,c[N];
vector<int> v[N];

class graph{
    int dfn[N];

    public:

    void tarjan(int x){
        static int d,low[N];
        static bool u[N];
        static stack<int> s;
        dfn[x]=low[x]=++d, u[x]=1, s.push(x);

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(!dfn[y]) tarjan(y), low[x]=min(low[x],low[y]);
            else if(u[y]) low[x]=min(low[x],dfn[y]);
        }

        if(dfn[x]==low[x]){
            c[x]=++p, v[p].push_back(x), u[x]=0;
            while(s.top()!=x){
                int y=s.top();
                s.pop();
                c[y]=p, v[p].push_back(y), u[y]=0;
            }
            s.pop();
        }
    }

    void scc(){
        for(int i=1; i<=n; i++){
            if(!dfn[i]) tarjan(i);
        }
    }
};

缩点

int n,p,a[N],d[N];

class graph{
    static int dfn[N],c[N],w[N];

    public:

    void tarjan(int x){
        static int d,low[N];
        static bool u[N];
        static stack<int> s;
        dfn[x]=low[x]=++d, u[x]=1, s.push(x);

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(!dfn[y]) tarjan(y), low[x]=min(low[x],low[y]);
            else if(u[y]) low[x]=min(low[x],dfn[y]);
        }

        if(dfn[x]==low[x]){
            c[x]=++p, w[p]=a[x], u[x]=0;
            while(s.top()!=x){
                int y=s.top();
                s.pop();
                c[y]=p, w[p]+=a[y], u[y]=0;
            }
            s.pop();
        }
    }

    void scc(){
        for(int i=1; i<=n; i++){
            if(!dfn[i]) tarjan(i);
        }
    }

    void rebuild(int x, graph &g){
        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(c[x]!=c[y]) g.add(c[x],c[y]);
        }
    }

    void topo(int x){
        d[x]+=w[x];
        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            d[y]=max(d[y],d[x]);
        }
    }
};

int graph::dfn[N],graph::c[N],graph::w[N];

双连通分量

点双连通分量

int n,p;
vector<int> v[N];

class graph{
    int dfn[N];

    public:

    void tarjan(int x, int f){
        static int d,low[N];
        static stack<int> s;
        dfn[x]=low[x]=++d, s.push(x);

        if(!f && !h[x]){
            v[++p].push_back(x);
            return;
        }

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(!dfn[y]){
                tarjan(y,x), low[x]=min(low[x],low[y]);
                if(low[y]<dfn[x]) continue;

                v[++p].push_back(x), v[p].push_back(y);
                while(s.top()!=y){
                    int z=s.top();
                    s.pop();
                    v[p].push_back(z);
                }
                s.pop();
            }
            else if(y!=f) low[x]=min(low[x],dfn[y]);
        }
    }

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

边双连通分量

int n,p;
vector<int> v[N];

class graph{
    int dfn[N];

    void add_one(int x, int y, int w){
        static int c=1;
        e[++c]={y,h[x],w}, h[x]=c;
    }

    public:

    void tarjan(int x){
        static int d,low[N];
        static bool u[M];
        static stack<int> s;
        dfn[x]=low[x]=++d, s.push(x);

        for(int i=h[x]; i; i=e[i].p){
            if(u[i]) continue;
            u[i]=u[i^1]=1;

            int y=e[i].t;
            if(!dfn[y]) tarjan(y), low[x]=min(low[x],low[y]);
            else low[x]=min(low[x],dfn[y]);
        }

        if(dfn[x]==low[x]){
            v[++p].push_back(x);
            while(s.top()!=x){
                int y=s.top();
                s.pop();
                v[p].push_back(y);
            }
            s.pop();
        }
    }

    void ebcc(){
        for(int i=1; i<=n; i++){
            if(!dfn[i]) tarjan(i);
        }
    }
};

割点和桥

割点

int n;
bool u[N];

class graph{
    int dfn[N];

    public:

    void tarjan(int x, int f){
        static int d,low[N];
        dfn[x]=low[x]=++d;

        int c=0;
        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(!dfn[y]){
                tarjan(y,x), low[x]=min(low[x],low[y]);
                if(!f) c++;
                else if(low[y]>=dfn[x]) u[x]=1;
            }
            else if(y!=f) low[x]=min(low[x],dfn[y]);
        }
        if(!f && c>1) u[x]=1;
    }

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

typedef pair<int,int> edge;
#define x first
#define y second

int n,c;
edge a[N];

class graph{
    int dfn[N];

    public:

    void tarjan(int x){
        static int d,low[N],f[N];
        dfn[x]=low[x]=++d;

        for(int i=h[x]; i; i=e[i].p){
            int y=e[i].t;
            if(!dfn[y]){
                f[y]=x, tarjan(y), low[x]=min(low[x],low[y]);
                if(low[y]>dfn[x]) a[++c]={x,y};
            }
            else if(y!=f[x]) low[x]=min(low[x],dfn[y]);
        }
    }

    void bri(){
        for(int i=1; i<=n; i++){
            if(!dfn[i]) tarjan(i);
        }
    }
};

2-SAT

class graph{
    public:

    void scc(){
        for(int i=1; i<=n<<1; i++){
            if(!dfn[i]) tarjan(i);
        }
    }
}g;

int main(){
    while(m--){
        int i,a,j,b;
        scanf("%d%d%d%d",&i,&a,&j,&b);
        g.add(i+n*!a,j+n*b),g.add(j+n*!b,i+n*a);
    }

    g.scc(i);

    for(int i=1; i<=n; i++){
        if(c[i]==c[i+n]){
            puts("IMPOSSIBLE");
            return 0;
        }
    }
    puts("POSSIBLE");
    for(int i=1; i<=n; i++) printf("%d ",c[i]>c[i+n]);
}

网络流

最大流

const int INF=0x3f3f3f3f;

class graph{
    int d[N];

    void add_one(int x, int y, int w){
        static int c=1;
        e[++c]={y,h[x],w}, h[x]=c;
    }

    bool bfs(int s, int t){
        queue<int> q;
        memset(d,0,sizeof d), d[s]=1, q.push(s);

        do{
            int x=q.front();
            q.pop();
            for(int i=h[x]; i; i=e[i].p){
                int y=e[i].t;
                if(!d[y] && e[i].w) d[y]=d[x]+1, q.push(y);
            }
        }while(!q.empty());

        return d[t];
    }

    int dfs(int x, int t, int l){
        if(x==t) return l;

        int c=0;
        for(int i=h[x]; i && c<l; i=e[i].p){
            int y=e[i].t;
            if(d[y]==d[x]+1 && e[i].w){
                int s=dfs(y,t,min(e[i].w,l-c));
                c+=s, e[i].w-=s, e[i^1].w+=s;
            }
        }

        if(!c) d[x]=-1;
        return c;
    }

    public:

    void add(int x, int y, int w){
        add_one(x,y,w), add_one(y,x,0);
    }

    long long dinic(int s, int t){
        long long c=0;
        while(bfs(s,t)) c+=dfs(s,t,INF);
        return c;
    }
};

学习,便是发现自己越来越菜的过程
最后更新于 2025-11-20