1. 程式人生 > 其它 >P2272 [ZJOI2007]最大半連通子圖

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;
}