BZOJ 1093 ZJOI 2007 最大半连通子图 DP

75 篇文章 1 订阅
16 篇文章 0 订阅

求图的最大半连通子图(什么是半连通参考POJ 2762)。

由于在对原图缩点以后,半连通就变成DAG上的一条链。那么答案就是DAG上的最长链。一个DP即可。
注意要处理重边。。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
#define ms(a, b) memset(a, b, sizeof a)
const int N = 100004, M = 1000004;
int top, ts, in[N], belong[N], num[N], sol[N], dis[N], instack[N], stack[N], low[N], dfn[N], scc;
set<pair<int, int> > mp;

struct Graph {
    int h[N], p[M], v[M], cnt;
    Graph() { ms(h, 0); cnt = 0; }
    void add(int a, int b) {
        p[++cnt] = h[a]; v[cnt] = b; h[a] = cnt;
    }
} g, c;

void tarjan(int x) {
    int i;
    dfn[x] = low[x] = ++ts;
    stack[++top] = x; instack[x] = 1;
    for (i = g.h[x]; i; i = g.p[i])
        if (!dfn[g.v[i]]) {
            tarjan(g.v[i]);
            low[x] = min(low[x], low[g.v[i]]);
        } else if (instack[g.v[i]])
            low[x] = min(low[x], dfn[g.v[i]]);
    if (dfn[x] == low[x]) {
        ++scc;
        do {
            i = stack[top--];
            instack[i] = 0;
            belong[i] = scc;
            ++num[scc];
        } while (i != x);
    }
}

void bfs(int x) {
    static int q[N];
    int i, f = 0, r = 0;
    FOR(i,1,scc) dis[i] = 0, sol[i] = 0;
    FOR(i,1,scc) if (!in[i]) q[r++] = i, dis[i] = 0, sol[i] = 1;
    while (f < r) {
        int u = q[f++];
        dis[u] += num[u];
        for (i = c.h[u]; i; i = c.p[i]) {
            if (dis[c.v[i]] < dis[u]) {
                dis[c.v[i]] = dis[u];
                sol[c.v[i]] = sol[u];
            } else if (dis[c.v[i]] == dis[u])
                (sol[c.v[i]] += sol[u]) %= x;
            if (--in[c.v[i]] <= 0) q[r++] = c.v[i];
        }
    }
}

int main() {
    int a, b, i, j, n, m, x, k = 0, l = 0;
    scanf("%d%d%d", &n, &m, &x);
    ts = top = scc = 0;
    FOR(i,1,m) scanf("%d%d", &a, &b), g.add(a, b);
    FOR(i,1,n) if (!dfn[i]) tarjan(i);
    FOR(i,1,n) for (j = g.h[i]; j; j = g.p[j])
        if (belong[i] != belong[g.v[j]] && !mp.count(make_pair(belong[i], belong[g.v[j]]))) {
            mp.insert(make_pair(belong[i], belong[g.v[j]]));
            ++in[belong[g.v[j]]]; c.add(belong[i], belong[g.v[j]]);
        }
    bfs(x);
    FOR(i,1,scc) if (!c.h[i]) k = max(k, dis[i]);
    FOR(i,1,scc) if (dis[i] == k) (l += sol[i]) %= x;
    printf("%d\n%d\n", k, l);
    return 0;
}

1093: [ZJOI2007]最大半连通子图

Time Limit: 30 Sec Memory Limit: 162 MB
Submit: 2449 Solved: 972

Description

Input

第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述。接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。

Output

应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.

Sample Input

6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4

Sample Output

3
3

HINT

对于100%的数据, N ≤100000, M ≤1000000;对于100%的数据, X ≤10^8。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值