最大半连通子图(luogu,ybtoj,acwing均有)

题意分析:发现,最大半连通子图与强连通分量类似,也是一种强连通分量,求最大数量,就是求出本图中缩点后的一个单链,找出其中权值最大的,可以运用拓扑dp来实现,设f[v]是以v为终点的最大权值,ans为点数(题目答案),d【v】为点v权值;

状转:f[v]=f[u]+d[v],ans[v]=ans[u](f[v]<f[u]+d[v);

        ans[v]+=ans[u](f[v]==f[u]+d[v])

强连通分量的思考题:

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int que[N], dfn[N], low[N], stak[N], col[N], num[N], mod, ru[N], f[N], ans[N], f1, ans1 = 0;
int qr, tot, time1 = 0, n, m, top = 0;
vector<int> e1[N], e2[N];
inline void tarjan(int x) {
    dfn[x] = low[x] = ++time1;
    stak[++top] = x;
    for (int v : e1[x]) {
        if (!dfn[v]) {
            tarjan(v);
            low[x] = min(low[x], low[v]);
        } else if (!col[v]) {
            low[x] = min(low[x], dfn[v]);
        }
    }
    if (dfn[x] == low[x]) {
        col[x] = ++tot;
        num[tot] = 1;
        int y;
        while (y = stak[top--], y != x) {
            col[y] = tot, num[tot]++;
        }
    }
    return;
}
int main() {
    cin >> n >> m >> mod;
    for (int i = 1, x, y; i <= m; i++) {
        cin >> x >> y;
        e1[x].push_back(y);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) {
            tarjan(i);//缩点
        }
    }
    for (int x = 1; x <= n; x++) {
        for (int y : e1[x]) {
            if (col[x] != col[y]) {
                e2[col[x]].push_back(col[y]);//新建一个有向无环图
            }
        }
    }
    for (int x = 1; x <= tot; x++) {
        sort(e2[x].begin(), e2[x].end());
        e2[x].erase(unique(e2[x].begin(), e2[x].end()), e2[x].end());
        for (int y : e2[x]) {
            ++ru[y];
        }
    }
    for (int x = 1; x <= tot; x++) {
        if (!ru[x]) {
            que[++qr] = x, f[x] = num[x], ans[x] = 1;
        }
    }
    for (int i = 1, x; i <= qr; i++) {
        x = que[i];
        for (int y : e2[x]) {
            if (f[y] < f[x] + num[y]) {
                f[y] = f[x] + num[y];
                ans[y] = ans[x];
            } else if (f[y] == f[x] + num[y]) {
                (ans[y] += ans[x]) %= mod;//拓扑dp
            }
            if (!--ru[y])
                que[++qr] = y;
        }
    }
    for (int x = 1; x <= tot; x++) {
        if (f[x] > f1) {
            f1 = f[x], ans1 = ans[x];
        } else if (f[x] == f1) {
            (ans1 += ans[x]) %= mod;//合并答案
        }
    }
    cout << f1 << endl << ans1 << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值