P2272 [ZJOI2007]最大半连通子图

Problem

当一个有向图\(G= (V,E)\)满足:\(\forall u,v \in V,u \to v\)\(v \to u\)
求这个图的最大半联通子图(节点数最多)节点数和最大半联通子图个数模\(X\)的值。
\(n \le 10^5,m \le 10^6,X \le 10^8\)

Solution

显然SCC也是强连通子图。所以先用Tarjan缩点,然后将图变成一个DAG(套路)。问题转变成:求缩点完后的DAG中的最长链最长链个数。显然可以拓扑+dp。

注意:缩点后会出现重边,要注意不能让重边加入,用个map记录即可。

# include <bits/stdc++.h>
using namespace std;
# define int long long
const int N = 1e5 + 5;
int n,m,X;
vector <int> g[N],g2[N];
vector <pair<int,int> > G;
int dfn[N],low[N],dfntot = 0,s[N],top = 0,scc_tot = 0,siz[N],belong[N],du[N];
bool vis[N]; int dis[N],cnt[N];
map<int,map<int,int> > check;
void dfs(int x)
{
    dfn[x] = low[x] = ++dfntot;
    s[++top] = x; vis[x] = 1;
    for(int i = 0; i < (int)g[x].size(); i++)
    {
        int v = g[x][i];
        if(!dfn[v]) 
        {
            dfs(v); low[x] = min(low[x],low[v]);
        }
        else if(vis[v]) low[x] = min(low[x],dfn[v]);
    }
    if(dfn[x] == low[x])
    {
        int temp = 0;
        ++scc_tot;
        do
        {   
            temp = s[top--];
            ++siz[scc_tot]; belong[temp] = scc_tot;
            vis[temp] = 0;
        }while(x != temp);
    }
    return;
}
void topsort(void)
{
    queue <int> q;
    for(int i = 1; i <= scc_tot; i++)
    {
        if(du[i] == 0) {q.push(i); dis[i] = siz[i],cnt[i] = 1;}
    }
    while(!q.empty())
    {
        int x = q.front(); q.pop();
        for(int i = 0; i < (int)g2[x].size(); i++)
        {
            int v = g2[x][i];
            if(siz[v] + dis[x] > dis[v]) 
            {
                dis[v] = dis[x] + siz[v];
                cnt[v] = cnt[x];
            }
            else if(siz[v] + dis[x] == dis[v])
            {
                cnt[v] = (cnt[v] + cnt[x]) % X;
            }
            if(--du[v] == 0) q.push(v);
        }
    }
    return;
}
signed main(void)
{
    scanf("%lld%lld%lld",&n,&m,&X);
    for(int i = 1; i <= m; i++)
    {
        int a,b; scanf("%lld%lld",&a,&b);
        g[a].push_back(b);
        G.push_back(make_pair(a,b));
    }
    for(int i = 1; i <= n; i++)
    {
        if(!dfn[i]) dfs(i);
    }
    for(int i = 0; i < m; i++)
    {
        int u = belong[G[i].first],v = belong[G[i].second];
        if(u != v && !check[u][v]) g2[u].push_back(v),du[v]++,check[u][v] = 1;
    }
    topsort();
    int ans1 = 0,ans2 = 0;
    for(int i = 1; i <= scc_tot; i++)
    {
        if(ans1 < dis[i]) {ans1 = dis[i]; ans2 = cnt[i] % X;}
        else if(ans1 == dis[i]) {ans2 = (ans2 + cnt[i]) % X;}
    }
    printf("%lld\n%lld",ans1 % X,ans2);
    return 0;
}
posted @ 2021-07-29 17:46  luyiming123  阅读(35)  评论(0编辑  收藏  举报