zoukankan      html  css  js  c++  java
  • 洛谷

    https://www.luogu.org/problem/P3469

    翻译:一个原本连通的无向图,可以删除图中的一个点,求因为删除这个点所导致的不连通的有序点对的数量。或者说,删去这个点之后,各个连通分量的大小的乘积之和?

    当然是考虑新学的Tarjan法求割点。一遍Tarjan给每个点记录他是不是割点。然后第二遍的时候对每个割点,统计它分割出的各个子树(及其父亲,假如有的话)这些连通块之间的贡献。

    注意无向图是不需要栈的,因为无向图不存在横向边的说法。

    错误代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int MAXN = 100005;
    
    int n;
    vector<int> G[MAXN];
    vector<int> T[MAXN];
    
    int dfn[MAXN], low[MAXN], dfncnt;
    bool cut[MAXN];
    int siz[MAXN];
    
    ll ans[MAXN];
    
    void tarjan(int u, int p) {
        low[u] = dfn[u] = ++dfncnt;
        siz[u] = 1;
        cut[u] = false;
        if(p != -1)
            T[u].push_back(p);
        int ch = 0;
        for(auto v : G[u]) {
            if(!dfn[v]) {
                tarjan(v, u);
                T[u].push_back(v);
                low[u] = min(low[u], low[v]);
                siz[u] += siz[v];
                if(p != -1 && low[v] >= dfn[u])
                    cut[u] = true;
                else if(p == -1)
                    ch++;
            } else
                low[u] = min(low[u], dfn[v]);
        }
        if(p == -1 && ch >= 2)
            cut[u] = true;
    }
    
    bool vis[MAXN];
    void dfs(int u, int p) {
        vis[u] = 1;
        for(auto v : T[u]) {
            if(!vis[v])
                dfs(v, u);
        }
        if(cut[u]) {
            ll sum = 0;
            ans[u] = 0;
            for(auto v : T[u]) {
                if(v == p) {
                    sum += n - siz[u];
                    ans[u] -= 1ll * (n - siz[u]) * (n - siz[u]);
                } else {
                    sum += siz[v];
                    ans[u] -= 1ll * siz[v] * siz[v];
                }
            }
            ans[u] += sum * sum + 2ll * sum;
        } else
            ans[u] = 2ll * (n - 1);
    }
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
    #endif // Yinku
        int m;
        scanf("%d%d", &n, &m);
        for(int i = 1, u, v; i <= m; ++i) {
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        tarjan(1, -1);
        dfs(1, -1);
        for(int i = 1; i <= n; ++i) {
            printf("%lld
    ", ans[i]);
        }
        return 0;
    }
    

    错误原因:某个节点u的子树v中可能出现了反向边(反向到u之前),这棵子树则和u节点的父亲节点等形成了连通块,假如要分段统计,则要在u节点标记哪些子树才是真正会被分开的子树。

    那么在这个问题里面对于根节点来说,每棵子树是必定会被分开的,可以统一处理掉。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int MAXN = 100005;
    
    int n;
    vector<int> G[MAXN];
    
    int dfn[MAXN], low[MAXN], dfncnt;
    
    ll ans[MAXN];
    int siz[MAXN];
    
    void tarjan(int u, int p) {
        low[u] = dfn[u] = ++dfncnt;
        siz[u] = 1;
        ll sum=0;
        int ch = 0;
        for(auto v : G[u]) {
            if(!dfn[v]) {
                tarjan(v, u);
                low[u] = min(low[u], low[v]);
                siz[u] += siz[v];
                if(low[v] >= dfn[u]){
                    ans[u]+=sum*siz[v];
                    sum+=siz[v];
                }
            } else
                low[u] = min(low[u], dfn[v]);
        }
        ans[u]+=(n-1-sum)*sum;
        ans[u]+=(n-1);
        ans[u]*=2ll;
    }
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
    #endif // Yinku
        int m;
        scanf("%d%d", &n, &m);
        for(int i = 1, u, v; i <= m; ++i) {
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        tarjan(1, -1);
        for(int i = 1; i <= n; ++i)
            printf("%lld
    ", ans[i]);
    
        return 0;
    }
    
  • 相关阅读:
    机械奥妙
    双向可控硅
    开关电源
    阻容降压电路
    手机充电电源的电路原理
    运算放大电路
    剃须刀电路
    d039: 点的位置
    d029: 求出2-100之间的所有质数(素数)
    d023: 各位数字之和
  • 原文地址:https://www.cnblogs.com/Yinku/p/11361600.html
Copyright © 2011-2022 走看看