Strongly connected

强连通分量

我们利用强连通分量进行缩点。然后找入度为0或者出度为0的点。
他们其中一个一定是在我们最终的图中被孤立的点。

为什么要这样说呢?
因为你仔细想想啊。假如我没有边,我让你用最多的边连出一个不是强连通分量的图
你是不是孤立一个点,使他只有出度没有入度或者只有入度没有出度?
那,这里不也是一样的吗?!其实,顺着感觉走就可以了。
然后我们统计就可以了。我们想对于一个出度为零或者入度为零的点,孤立他得到的最大收益应该为:
n(n-1)-m-cnt[i](n-cnt[i]) cnt[i]指该连通块中的点的数量。
这里用的是割补法求的,刚开始我想直接求,麻烦得很。。。。。。。。
我们注意到,这个式子里面只有一个变量cnt[i]
设为x把他单独出出来看:x^2-n*x 对称轴为n/2
我们可以直接根据这个式子判断,但是其实我们直接取最小值就好了。
因为有一个点数为k的连通块那么就必定会有一个点数为n-k的连通块。而二者是相等的!

不细心,wa了好多发:(

代码如下:

#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
typedef long long ll;
const int max_m = 2e5 + 100;
const int max_n = 1e5 + 100;
struct edge{
    int to, next;
}E[max_m];
int head[max_n];
int cnt = 1;
void add(int from, int to) {
    E[cnt].to = to;
    E[cnt].next = head[from];
    head[from] = cnt++;
}

int cct[max_n];
stack<int> st;int tot = 0;
int dfn[max_n], low[max_n];
int col[max_n];int color = 0;
void tarjan(int u) {
    dfn[u] = low[u] = ++tot;
    st.push(u);
    for (int i = head[u];i;i = E[i].next) {
        int v = E[i].to;
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (col[v] == 0)low[u] = min(low[u], dfn[v]);
    }if (low[u] != dfn[u])return;
    ++color;
    while (st.top() != u) {
        col[st.top()] = color;
        st.pop();
        ++cct[color];
    }col[st.top()] = color;
    st.pop();++cct[color];
}
int in[max_n], out[max_n];
int N, M;
int main() {
    int T;scanf("%d", &T);
    for (int tcase = 1;tcase <= T;++tcase) {
        scanf("%d %d", &N, &M);
        fill(head, head + N + 10, 0);cnt = 1;
        fill(dfn, dfn + N + 10, 0);
        fill(col, col + N + 10, 0);
        fill(cct, cct + N + 10, 0);
        fill(in, in + N + 10, 0);
        fill(out, out + N + 10, 0);
        color = tot = 0;
        for (int i = 1, u, v;i <= M;++i) {
            scanf("%d %d", &u, &v);
            add(u, v);
        }for (int i = 1;i <= N;++i)
            if (!dfn[i])
                tarjan(i);
        if (color == 1) {
            printf("Case %d: -1\n", tcase);
            continue;
        }for (int u = 1;u <= N;++u) {
            for (int i = head[u];i;i = E[i].next) {
                int v = E[i].to;
                if (col[v] == col[u])continue;
                ++in[col[v]];++out[col[u]];
            }
        }
        ll ans = 0;ll tmp = N;
        for (int i = 1;i <= color;++i)
            if (in[i] == 0 || out[i] == 0)
                tmp = min(tmp, (ll)cct[i]);
        ans = (ll)N * ((ll)N - 1) - ((ll)N - tmp) * tmp - M;
        printf("Case %d: %lld\n", tcase, ans);
    }
}
kuangbin刷题题单详解(连通图) 文章被收录于专栏

题单:https://vjudge.net/article/371

全部评论

相关推荐

11-09 14:54
已编辑
华南农业大学 产品经理
大拿老师:这个简历,连手机号码和照片都没打码,那为什么关键要素求职职位就不写呢? 从上往下看,都没看出自己到底是产品经理的简历,还是电子硬件的简历? 这是一个大问题,当然,更大的问题是实习经历的描述是不对的 不要只是去写实习流程,陈平,怎么去开会?怎么去讨论? 面试问的是你的产品功能点,是怎么设计的?也就是要写项目的亮点,有什么功能?这个功能有什么难处?怎么去解决的? 实习流程大家都一样,没什么优势,也没有提问点,没有提问,你就不得分 另外,你要明确你投的是什么职位,如果投的是产品职位,你的项目经历写的全都是跟产品无关的,那你的简历就没用 你的面试官必然是一个资深的产品经理,他不会去问那些计算机类的编程项目 所以这种四不像的简历,在校招是大忌
点赞 评论 收藏
分享
2 收藏 评论
分享
牛客网
牛客企业服务