• 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,深度优先遍历整个图,只要从强连通分量的数量倒着枚举到一,就是缩点后形成的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
    %d
    ",k,ans);
        return 0;
    }
  • 相关阅读:
    web 单例 多例
    python socket客户端
    foy: 轻量级的基于 nodejs 的通用 build 工具
    Hydux: 一个 Elm-like 的 全功能的 Redux 替代品
    AlarmManager使用注意事项
    【转】android ListView 几个重要属性
    自己写的小工具软件集合
    win8.1 cygwin编译java轻量虚拟机avian
    android 图片缩放抗锯齿
    windows phone和android,ios的touch事件兼容
  • 原文地址:https://www.cnblogs.com/kjd123456/p/12616743.html
Copyright © 2020-2023  润新知