图论1-图的连通性相关

luogu P2272 最大半连通子图

  • 题意描述:一个有向图 \(G=\left(V,E\right)\)称为半连通的 ,如果满足:\(\forall u,v\in V\),存在一条 \(u\)\(v\) 的有向路径或者从 \(v\)\(u\) 的有向路径。称\(G'=(V',E)\)\(G\)的导出子图如有\(V'\)\(G\)中所有有关边均在\(G'\)​中。若G′是G的导出子图,且G′半连通,则称G‘为G的半连通子图。若 G’ 是 G 所有半连通子图中包含节点数最多的,则称G′是G的最大半连通子图。给定有向图\(G\),求其最大半连通子图的节点数\(K\)以及不同的最大半连通子图的数目\(C\)
  • 因对于一个强连通分量而言,若可以存在一条路径\(uv\)连接强连通分量外的点\(u\),与强连通分量内的点\(v\),则显然\(u\)可以到达该强连通分量内的任何点。所以若是选取强连通分量中的一个点,显然可以选取整个强连通分量。所以我们可以通过\(tarjan\)算法缩点来创造一个\(DAG\)
  • 得到\(DAG\)后,因为\(tarjan\)算法所得到的强连通分量编号逆序即为拓扑序,我们直接对强连通分量重新以链式前向星建图,并且在建图的过程中通过\(hash\)来去除重边,避免对结果的计算产生影响。建图之后强连通分量编号即为图中顶点的编号。
  • 而因为我们所求的最大半连通子图中任意两点之间都有路径,所以我们所求的最大半连通子图只能是所得拓扑图中的一条链。
  • 建图之后我们按照拓扑序遍历所有节点,采用类似动态规划的方式进行答案的求解,规定函数\(f\)记录下当前子图的节点数目,\(g\)记录下与当前子图节点数目相同情况下的方案数
  • 状态转移方程

\[g(k)= \left\{\begin{array}{lcl} g(i),&f(k)<f(i)+size(i)\\ g(i)+g(k),&f(k)=f(i)+size(i) \end{array}\right. \\ f(k)= \left\{\begin{array}{lcl} f(i)+size(i),&f(k)<f(i)+size(i)\\ f(i) ,&else \end{array}\right. \]

  • 其中\(i\)\(k\)之间存在边\(i\to k\)
  • 所以我们有代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5,maxm=1e6+5;
// 强连通分量算法完成后即可得到拓扑序,不需要再进行拓扑排序
#define ll long long
int n,m,mod;

int tim=0,top=0,tot=0;;
int dfn[maxn],low[maxn],s[maxn],vis[maxn],id[maxn],size[maxn];

struct edge
{
    int u,v,nxt;
}e[maxm],et[maxm];
int head[maxm],headt[maxm];
int cnt=0,cntt=0;

void addedge(int u,int v){
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}

void tarjan(int u){
    low[u]=dfn[u]=++tim;
    s[++top]=u;
    vis[u]=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }else if(vis[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        int y;
        tot++;
        do {
            y = s[top--];
            vis[y] = 0;
            id[y] = tot;
            size[tot] ++ ;
        } while (y != u);
    }
}

int main(){
    scanf("%d %d %d",&n,&m,&mod);
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i])tarjan(i);
    }

    unordered_set<ll> st;
    for(int u=1;u<=n;u++){
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].v;
            int a=id[u],b=id[v];
            ll hasher = a*1000000ll+b;
            if(a!=b && !st.count(hasher)){
                st.insert(hasher);
                et[++cntt].u=a;
                et[cntt].v=b;
                et[cntt].nxt=headt[a];
                headt[a]=cntt;
            }
        }
    }
    int f[maxn],g[maxn];
    for(int i=tot;i;i--){
        if(!f[i]){
            f[i]=size[i];
            g[i]=1;
        }
        for(int j=headt[i];j;j=et[j].nxt){
            int k=et[j].v;
            if(f[k]<f[i]+size[k])
            {
                f[k]=f[i]+size[k];
                g[k]=g[i];
            }else if(f[k]==f[i]+size[k]){
                g[k]=(g[k]+g[i])%mod;
            }
        }
    }
    int maxe=0,sum=0;
    for(int i=1;i<=tot;i++){
        if(f[i]>maxe){
            maxe=f[i];
            sum=g[i];
        }else if(f[i]==maxe)sum=(sum+g[i])%mod;
    }
    cout<<maxe<<endl;
    cout<<sum<<endl;
    return 0;
}
posted @ 2021-12-21 18:01  Ghaser  阅读(39)  评论(0)    收藏  举报