NOIP模拟 Date - Tarjan

双联通分量求解

题目描述

小Y和小Z好不容易有机会相见啦,可是邪恶的小H却不想让他们相见。现在有一些城市,城市之间有双向路径相连,有路径相连的城市之间可以互相到达。小H可以任意选择一条路径,然后用他的邪恶力量污染这条路径,使得它不能被通行。虽然小Y和小Z在千辛万苦之后相遇了,但小Y非常害怕。她想让小Z告诉她,他们初始在哪些点对上,小H就可以选择一条路径污染使得他们不能相见。

注意:如果有一对点之间初始的时候就不联通,也是满足条件的,需要输出这对点。这是因为本来不联通,那么删一条边,当然也不联通。

输入格式

第一行两个数字 N 和 M 。N 表示城市数,M 表示路径数。
第二行到第 M+1 行,两个数 a 和 b。其中 1≤a,b≤N ,表示 a 和 b 之间有路径相连。

输出格式

输出一个整数,表示所求点对的数量

样例数据 1

输入  [复制]

2 1
1 2

输出

1

备注

【样例说明】

点对(1,2)满足不能相见的条件。

【数据范围】

对 30% 的输入数据 :1≤N≤100,1≤M≤200
对 100% 的输入数据 :1≤N≤20000,1≤M≤40000

题目分析

直接求不连通的不是很容易,考虑用总数减去联通的。剩下的就是裸的tarjan求无向图的双联通,联通块内所有点都可以互相联通,总数减去s * (s - 1) / 2即可。

code

#include<bits/stdc++.h>
using namespace std;

const int N = 20005, M = 40005;

int n, m;
int ecnt = 1, adj[N], nxt[M << 1], go[M << 1];
int scccnt, sccno[N];
long long sccNum[N], ans;
int low[N], dfn[N], clk;
stack<int> S;

inline void dfs(int u, int ee){
    dfn[u] = low[u] = ++clk;
    S.push(u);
    
    for(int e = adj[u]; e != -1; e = nxt[e]){
        if(e == (ee ^ 1)) continue;
        int v = go[e];
        if(!dfn[v]){
            dfs(v, e);
            low[u] = min(low[u], low[v]);
        }
        else if(!sccno[v])
            low[u] = min(low[u], dfn[v]); 
    }
    
    if(low[u] == dfn[u]){
        int x;
        scccnt++;
        for(;;){
            x = S.top(); S.pop();
            sccNum[scccnt]++;
            sccno[x] = scccnt;
            if(x == u) break;
        }
        ans -= sccNum[scccnt] * (sccNum[scccnt] - 1) / 2;
    }
}

inline void addEdge(int u, int v){
    nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v;
    nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    cin >> n >> m;
    memset(adj, -1, sizeof adj);
    for(int i = 1; i <= m; i++){
        int a, b; cin >> a >> b;
        addEdge(a, b);
    }
    ans = n  * (n - 1) / 2;
    for(int i = 1; i <= n; i++)
        if(!dfn[i]) dfs(i, -1);
    cout << ans << endl;
    return 0;
}

转载于:https://www.cnblogs.com/CzYoL/p/7637748.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值