【算法提高——第三讲(二)】图论(2)

简介: 【算法提高——第三讲(二)】图论(2)

3.9 最近公共祖先

3.9.1 1172. 祖孙询问

image.png


代码:


#include<bits/stdc++.h>
using namespace std;
const int N=40010,M=80010;
int n,m;
int fa[N][16],depth[N];
int h[N],e[M],ne[M],idx;
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs(int root)
{
    memset(depth,0x3f,sizeof depth);
    depth[0]=0,depth[root]=1;
    queue<int> q;
    q.push(root);
    while(q.size())
    {
        int t=q.front();
        q.pop();
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(depth[j]>depth[t]+1)
            {
                depth[j]=depth[t]+1;
                fa[j][0]=t;
                q.push(j);
                for(int k=1;k<=15;k++)
                {
                    fa[j][k]=fa[fa[j][k-1]][k-1];
                }
            }
        }
    }
}
int lca(int a,int b)
{
    if(depth[a]<depth[b]) swap(a,b);
    for(int k=15;k>=0;k--)
    {
        if(depth[fa[a][k]]>=depth[b])
            a=fa[a][k];
    }
    if(a==b) return a;
    for(int k=0;k<=15;k++)
    {
        if(fa[a][k]!=fa[b][k])
        {
            a=fa[a][k];
            b=fa[b][k];
        }
    }
    return fa[a][0];
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n;
    int root;
    for(int i=0;i<n;i++)
    {
        int a,b;
        cin>>a>>b;
        if(b==-1) root=a;
        else
        {
            add(a,b),add(b,a);
        }
    }
    bfs(root);
    cin>>m;
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        int p=lca(a,b);
        if(p==a) cout<<1<<endl;
        else if(p==b) cout<<2<<endl;
        else cout<<0<<endl;
    }
    return 0;
}

3.9.2 1171. 距离

image.png


代码:


#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;//first保存相关的查询节点编号,second保存查询编号
const int N=10010,M=20010;
int n,m;
int dis[N];
int p[N];
int res[M];
int st[N];
vector<PII> query[N];
int h[N],e[M],w[M],ne[M],idx;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int findP(int x)
{
    if(p[x]!=x) return p[x]=findP(p[x]);
    return p[x];
}
void dfs(int u,int fa)
{
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j==fa) continue;
        dis[j]=dis[u]+w[i];
        dfs(j,u);
    }
}
void tarjan(int u)
{
    st[u]=1;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!st[j])
        {
            tarjan(j);
            p[j]=u;
        }
    }
    for(int i=0;i<query[u].size();i++)
    {
        PII t=query[u][i];
        int y=t.first,id=t.second;
        if(st[y]==2)
        {
            int anc=findP(y);
            res[id]=dis[u]+dis[y]-2*dis[anc];
        }
    }
    st[u]=2;
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=0;i<n-1;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    for(int i=0;i<m;i++)
    {
        int a,b;
        cin>>a>>b;
        if(a!=b)
        {
            query[a].push_back(make_pair(b,i));
            query[b].push_back(make_pair(a,i));
        }
    }
    for(int i=1;i<=n;i++) p[i]=i;
    dfs(1,-1);
    tarjan(1);
    for(int i=0;i<m;i++)
    {
        cout<<res[i]<<endl;
    }
    return 0;
}

3.9.3 356. 次小生成树

image.png


代码:


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100010,M=300010,INF=0x3f3f3f3f;
int n,m;
int p[N];
int depth[N],fa[N][17],d1[N][17],d2[N][17];
int q[N];
struct Edge{
    int a,b,w;
    bool used;
    bool operator < (const Edge &t) const
    {
        return w<t.w;
    }
}edge[M];
int h[N],e[M],w[M],ne[M],idx;
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int findP(int x)
{
    if(p[x]!=x) return p[x]=findP(p[x]);
    return p[x];
}
LL kruskal()
{
    for(int i=1;i<=n;i++) p[i]=i;
    sort(edge,edge+m);
    LL res=0;
    for(int i=0;i<m;i++)
    {
        int a=findP(edge[i].a),b=findP(edge[i].b),w=edge[i].w;
        if(a!=b)
        {
            p[a]=b;
            res+=w;
            edge[i].used=true;
        }
    }
    return res;
}
void build()
{
    memset(h,-1,sizeof h);
    for(int i=0;i<m;i++)
    {
        if(edge[i].used)
        {
            int a=edge[i].a,b=edge[i].b,w=edge[i].w;
            add(a,b,w),add(b,a,w);
        }
    }
}
void bfs()
{
    memset(depth,0x3f,sizeof depth);
    depth[0]=0,depth[1]=1;
    queue<int> q;
    q.push(1);
    while(q.size())
    {
        int t=q.front();
        q.pop();
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(depth[j]>depth[t]+1)
            {
                depth[j]=depth[t]+1;
                q.push(j);
                fa[j][0]=t;
                d1[j][0]=w[i],d2[j][0]=-INF;
                for(int k=1;k<=16;k++)
                {
                    int anc=fa[j][k-1];
                    fa[j][k]=fa[anc][k-1];
                    int distance[4]={d1[j][k-1],d2[j][k-1],d1[anc][k-1],d2[anc][k-1]};
                    d1[j][k]=d2[j][k]=-INF;
                    for(int u=0;u<4;u++)
                    {
                        int d=distance[u];
                        if(d>d1[j][k]) d2[j][k]=d1[j][k],d1[j][k]=d;
                        else if(d!=d1[j][k]&&d>d2[j][k]) d2[j][k]=d;
                    }
                }
            }
        }
    }
}
int lca(int a,int b,int w)
{
    static int distance[2*N];
    int cnt=0;
    if(depth[a]<depth[b]) swap(a,b);
    for(int k=16;k>=0;k--)
    {
        if(depth[fa[a][k]]>=depth[b])
        {
            distance[cnt++]=d1[a][k];
            distance[cnt++]=d2[a][k];
            a=fa[a][k];
        }
    }
    if(a!=b)
    {
        for(int k=16;k>=0;k--)
        {
            if(fa[a][k]!=fa[b][k])
            {
                distance[cnt++]=d1[a][k];
                distance[cnt++]=d2[a][k];
                distance[cnt++]=d1[b][k];
                distance[cnt++]=d2[b][k];
                a=fa[a][k],b=fa[b][k];
            }
        }
        distance[cnt++]=d1[a][0];
        distance[cnt++]=d1[b][0];
    }
    int dist1=-INF,dist2=-INF;
    for(int i=0;i<cnt;i++)
    {
        int d=distance[i];
        if(d>dist1) dist2=dist1,dist1=d;
        else if(d!=dist1&&d>dist2) dist2=d;
    }
    if(w>dist1) return w-dist1;
    if(w>dist2) return w-dist2;
    return INF;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        edge[i].a=a,edge[i].b=b,edge[i].w=c;
    }
    LL sum=kruskal();
    build();
    bfs();
    LL res=1e18;
    for(int i=0;i<m;i++)
    {
        if(!edge[i].used)
        {
            int a=edge[i].a,b=edge[i].b,w=edge[i].w;
            res=min(res,sum+lca(a,b,w));
        }
    }
    cout<<res;
    return 0;
}

3.9.4 352. 闇の連鎖

image.png


代码:


#include<bits/stdc++.h>
using namespace std;
const int N=100010,M=200010;
int n,m;
int depth[N],fa[N][17];
int d[N];
int ans;
int h[N],e[M],ne[M],idx;
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs()
{
    memset(depth,0x3f,sizeof depth);
    depth[0]=0,depth[1]=1;
    queue<int> q;
    q.push(1);
    while(q.size())
    {
        int t=q.front();
        q.pop();
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(depth[j]>depth[t]+1)
            {
                depth[j]=depth[t]+1;
                q.push(j);
                fa[j][0]=t;
                for(int k=1;k<=16;k++)
                {
                    fa[j][k]=fa[fa[j][k-1]][k-1];
                }
            }
        }
    }
}
int lca(int a,int b)
{
    if(depth[a]<depth[b]) swap(a,b);
    for(int k=16;k>=0;k--)
    {
        if(depth[fa[a][k]]>=depth[b])
            a=fa[a][k];
    }
    if(a==b) return a;
    for(int k=16;k>=0;k--)
    {
        if(fa[a][k]!=fa[b][k])
        {
            a=fa[a][k];
            b=fa[b][k];
        }
    }
    return fa[a][0];
}
int dfs(int u,int father)
{
    int res=d[u];
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j!=father)
        {
            int s=dfs(j,u);
            if(s==0) ans+=m;
            else if(s==1) ans++;
            res+=s;
        }
    }
    return res;
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=0;i<n-1;i++)
    {
        int a,b;
        cin>>a>>b;
        add(a,b),add(b,a);
    }
    bfs();
    for(int i=0;i<m;i++)
    {
        int a,b;
        cin>>a>>b;
        int p=lca(a,b);
        d[a]+=1,d[b]+=1,d[p]-=2;
    }
    dfs(1,-1);
    cout<<ans;
    return 0;
}

3.10 有向图的强连通分量

3.10.1 1174. 受欢迎的牛

image.png


代码:


#include<bits/stdc++.h>
using namespace std;
const int N=10010,M=50010;
int n,m;
int dfn[N],low[N],timestamp;
stack<int> stk;
bool in_stk[N];
int id[N],scc_cnt,sz[N];
int dout[N];
int h[N],e[M],ne[M],idx;
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++timestamp;
    stk.push(u),in_stk[u]=true;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u]=min(low[u],low[j]);
        }
        else if(in_stk[j])
            low[u]=min(low[u],dfn[j]);
    }
    if(dfn[u]==low[u])
    {
        ++scc_cnt;
        int y;
        do{
            y=stk.top();
            stk.pop();
            in_stk[y]=false;
            id[y]=scc_cnt;
            sz[scc_cnt]++;
        }while(y!=u);
    }
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        add(a,b);
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
            tarjan(i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=h[i];j!=-1;j=ne[j])
        {
            int k=e[j];
            int a=id[i],b=id[k];
            if(a!=b) dout[a]++;
        }
    }
    int zeros=0,sum=0;
    for(int i=1;i<=scc_cnt;i++)
    {
        if(!dout[i])
        {
            zeros++;
            sum+=sz[i];
            if(zeros>1)
            {
                sum=0;
                break;
            }
        }
    }
    cout<<sum;
    return 0;
}

3.10.2 367. 学校网络

image.png


代码:


#include<bits/stdc++.h>
using namespace std;
const int N=110,M=10010;
int n;
int dfn[N],low[N],timestamp;
stack<int> stk;
bool in_stk[N];
int id[N],scc_cnt,sz[N];
int din[N],dout[N];
int h[N],e[M],ne[M],idx;
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++timestamp;
    stk.push(u),in_stk[u]=true;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u]=min(low[u],low[j]);
        }
        else if(in_stk[j]) low[u]=min(low[u],dfn[j]);
    }
    if(dfn[u]==low[u])
    {
        ++scc_cnt;
        int y;
        do{
            y=stk.top();
            stk.pop();
            in_stk[y]=false;
            id[y]=scc_cnt;
            sz[scc_cnt]++;
        }while(y!=u);
    }
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        while(true)
        {
            int b;
            cin>>b;
            if(b==0) break;
            add(i,b);
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
            tarjan(i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=h[i];j!=-1;j=ne[j])
        {
            int k=e[j];
            int a=id[i],b=id[k];
            if(a!=b) din[b]++,dout[a]++;
        }
    }
    int p=0,q=0;
    for(int i=1;i<=scc_cnt;i++)
    {
        if(!din[i]) p++;
        if(!dout[i]) q++;
    }
    cout<<p<<endl;
    if(scc_cnt==1)
        cout<<0<<endl;
    else
        cout<<max(p,q)<<endl;
    return 0;
}

3.10.3 1175. 最大半连通子图

image.png


代码:


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100010,M=2000010;
int n,m,mod;
int dfn[N],low[N],timestamp;
stack<int> stk;
bool in_stk[N];
int id[N],scc_cnt,sz[N];
int f[N],g[N];
int h[N],hs[N],e[M],ne[M],idx;
void add(int h[],int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++timestamp;
    stk.push(u),in_stk[u]=true;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u]=min(low[u],low[j]);
        }
        else if(in_stk[j]) low[u]=min(low[u],dfn[j]);
    }
    if(dfn[u]==low[u])
    {
        ++scc_cnt;
        int y;
        do{
            y=stk.top();
            stk.pop();
            in_stk[y]=false;
            id[y]=scc_cnt;
            sz[scc_cnt]++;
        }while(y!=u);
    }
}
int main()
{
    memset(h,-1,sizeof h);
    memset(hs,-1,sizeof hs);
    cin>>n>>m>>mod;
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        add(h,a,b);
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
            tarjan(i);
    }
    unordered_set<LL> S;
    for(int i=1;i<=n;i++)
    {
        for(int j=h[i];j!=-1;j=ne[j])
        {
            int k=e[j];
            int a=id[i],b=id[k];
            LL ha=a*1000000ll+b;
            if(a!=b&&!S.count(ha))
            {
                add(hs,a,b);
                S.insert(ha);
            }
        }
    }
    for(int i=scc_cnt;i>0;i--)
    {
        if(!f[i])
        {
            f[i]=sz[i];
            g[i]=1;
        }
        for(int j=hs[i];j!=-1;j=ne[j])
        {
            int k=e[j];
            if(f[k]<f[i]+sz[k])
            {
                f[k]=f[i]+sz[k];
                g[k]=g[i];
            }
            else if(f[k]==f[i]+sz[k])
                g[k]=(g[k]+g[i])%mod;
        }
    }
    int maxf=0,sum=0;
    for(int i=1;i<=scc_cnt;i++)
    {
        if(f[i]>maxf)
        {
            maxf=f[i];
            sum=g[i];
        }
        else if(f[i]==maxf) sum=(sum+g[i])%mod;
    }
    cout<<maxf<<endl;
    cout<<sum<<endl;
    return 0;
}

3.10.4 368. 银河

image.png


代码:


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100010,M=600010;
int n,m;
int dfn[N],low[N],timestamp;
stack<int> stk;
bool in_stk[N];
int id[N],scc_cnt,sz[N];
int dis[N];
int h[N],hs[N],e[M],w[M],ne[M],idx;
void add(int h[],int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++timestamp;
    stk.push(u),in_stk[u]=true;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u]=min(low[u],dfn[j]);
        }
        else if(in_stk[j]) low[u]=min(low[u],dfn[j]);
    }
    if(dfn[u]==low[u])
    {
        ++scc_cnt;
        int y;
        do{
            y=stk.top();
            stk.pop();
            in_stk[y]=false;
            id[y]=scc_cnt;
            sz[scc_cnt]++;
        }while(y!=u);
    }
}
int main()
{
    memset(h,-1,sizeof h);
    memset(hs,-1,sizeof hs);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        add(h,0,i,1);
    while(m--)
    {
        int t,a,b;
        cin>>t>>a>>b;
        if(t==1) add(h,b,a,0),add(h,a,b,0);
        else if(t==2) add(h,a,b,1);
        else if(t==3) add(h,b,a,0);
        else if(t==4) add(h,b,a,1);
        else add(h,a,b,0);
    }
    tarjan(0);
    bool success=true;
    for(int i=0;i<=n;i++)
    {
        for(int j=h[i];j!=-1;j=ne[j])
        {
            int k=e[j];
            int a=id[i],b=id[k];
            if(a==b)
            {
                if(w[j]>0)
                {
                    success=false;
                    break;
                }
            }
            else add(hs,a,b,w[j]);
        }
        if(!success) break;
    }
    if(!success) cout<<-1<<endl;
    else
    {
        for(int i=scc_cnt;i>0;i--)
        {
            for(int j=hs[i];j!=-1;j=ne[j])
            {
                int k=e[j];
                dis[k]=max(dis[k],dis[i]+w[j]);
            }
        }
        LL res=0;
        for(int i=1;i<=scc_cnt;i++) res+=(LL)dis[i]*sz[i];
        cout<<res;
    }
    return 0;
}

相关文章
|
6月前
|
机器学习/深度学习 安全 算法
【图论】【割点】【C++算法】928. 尽量减少恶意软件的传播 II
【图论】【割点】【C++算法】928. 尽量减少恶意软件的传播 II
|
6月前
|
算法 测试技术 C++
【动态规划】【图论】【C++算法】1575统计所有可行路径
【动态规划】【图论】【C++算法】1575统计所有可行路径
|
存储 算法 索引
数据结构与算法之图论及其相关算法
图的基本介绍 线性表局限于一个直接前驱和一个直接后继的关系,树也只能有一个直接前驱也就是父节点,当我们需要表示多对多的关系时, 这里我们就用到了图。 图是一种数据结构,其中结点可以具有零个或多个相邻元素。两个结点之间的连接称为边。 结点也可以称为顶点。如图: 简单来说,图就是由顶点的有穷非空集合和顶点之间的边组成的集合。通常表示为:G(V,E),其中,G 表示一个图,V 表示顶点的集合,E 表示边的集合。 然后我们说说图中的一些常见概念: 节点(Vertex):图中的基本元素,用于表示某个实体。 边(Edge):连接两个节点的线段,用于表示节点之间的关系。 度(Degree):
56 0
|
6月前
|
算法 测试技术 C++
【动态规划】【图论】【C++算法】1928规定时间内到达终点的最小花费
【动态规划】【图论】【C++算法】1928规定时间内到达终点的最小花费
|
算法 存储 内存技术
【AcWing算法基础课】第三章 搜索与图论(2)
特点:尽可能先向纵深方向搜索。使用stack实现。所需空间O(h)(h为深度)。不具有“最短性”。
79 0
【AcWing算法基础课】第三章 搜索与图论(2)
|
5月前
|
算法 数据挖掘 定位技术
算法必备数学基础:图论方法由浅入深实践与应用
算法必备数学基础:图论方法由浅入深实践与应用
|
6月前
|
算法 C++
一题带你写出图论算法模板!!!
一题带你写出图论算法模板!!!
|
6月前
|
算法 搜索推荐 Python
Python高级数据结构——图论算法(Graph Algorithms)
Python高级数据结构——图论算法(Graph Algorithms)
159 0
|
6月前
|
算法 C++ NoSQL
|
6月前
|
存储 算法 定位技术
图论算法dijkstra dfs bfs以及动态规划
图论算法dijkstra dfs bfs以及动态规划
83 0