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;
}
posted @ 2017-10-08 16:28  CzYoL  阅读(195)  评论(1编辑  收藏  举报