Luogu P2272 / loj10092 / ZJOI2007最大半连通子图

题目地址

通过观察可得:

1.一个强连通分量为一个半连通子图


2.一条链为一个半连通子图

执行$tarjan$算法缩点后,得到的图是一个DAG,使每个点的权值为这个点所包含的点数,问题就转化成了求DAG上的最长链(链上的点权值和最大)的大小和个数。

既然是DAG,很明显可以想到DP。

$f[u]$为以点$u$结尾的链的最大长度,那么很明显,状态转移方程为$f[v] = max(f[v],f[u] + size[v])$。

$g[u]$为以点$u$结尾的最大链的个数,当$f[v] < f[u] + size[v]$时,$g[u] = f[u]$,当$f[v] = f[u] + size[v]$时,$g[u] = g[u] + 1$

这样去DP的话,是需要拓扑排序的,如果$tarjan$缩点后去处理每个点的入度明显会超时。

这里有一个技巧,因为$tarjan$是DFS,深度优先遍历整个图,每个强连通分量出栈的顺序就是拓扑序的倒序,因此DP时,只要从强连通分量的数量倒着枚举到一,就是缩点后形成的DAG的拓扑序。

另外注意DP时注意重边,要去重。

$Code:$

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <stack>
#include <cstring>

using namespace std;

const int MAXN = 100000 + 10;

int n,m,k,x;
int low[MAXN],dfn[MAXN],color[MAXN],nodecnt,cnt,f[MAXN],siz[MAXN],ans,num[MAXN];
int vis[MAXN];
vector<int> e[MAXN];
vector<int> en[MAXN];
stack<int> s;

void tarjan(int u) {
    dfn[u] = low[u] = ++cnt;
    s.push(u);
    int len = e[u].size();
    for(int i = 0; i < len; i++) {
        int v = e[u][i];
        if(!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(!color[v]){
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(low[u] == dfn[u]) {
        ++nodecnt;
        int t;
        do{
            siz[nodecnt]++;
            t = s.top();
            s.pop();
            color[t] = nodecnt;
        }while(t != u);
    }
}

void DP() {
    for(int i = nodecnt; i >= 1; i--) {
        int len = en[i].size();
        for(int j = 0; j < len; j++) {
            int v = en[i][j];
            if(vis[v] == i) continue;
            vis[v] = i;
            if(f[v] < f[i] + siz[v]) {
                f[v] = f[i] + siz[v];
                num[v] = num[i];
            }
            else if(f[v] == f[i] + siz[v]) {
                num[v] += num[i];
                num[v] %= x;
            }
        }
    }
}

int main() {
    scanf("%d%d%d",&n,&m,&x);
    for(int i = 1; i <= m; i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
    }
    for(int i = 1; i <= n; i++) {
        if(!dfn[i]) tarjan(i);
    }
    for(int i = 1; i <= n; i++) {
        int len = e[i].size();
        for(int j = 0; j < len; j++) {
            if(color[i] == color[e[i][j]]) continue;
            en[color[i]].push_back(color[e[i][j]]);
        }
    }
    for(int i = 1; i <= nodecnt; i++) {
        f[i] = siz[i];
        num[i] = 1;
    }
    DP();
    for(int i = 1; i <= nodecnt; i++) {
        if(f[i] > k) {
            k = f[i];
            ans = num[i];
            ans %= x;
        }
        else if(f[i] == k) {
            ans += num[i];
            ans %= x;
        }
    }
    printf("%d\n%d\n",k,ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kjd123456/p/12616743.html