Codeforces Round #469 (Div. 2) E. Data Center Maintenance (强连通分量、Tarjan)

传送门:http://codeforces.com/contest/950/problem/E

 

题目大意:

  一个公司有n个数据站,m条信息,每条信息都需要放在2个数据站里。每天有h个小时,每个数据站都在某一个小时需要维护,维护的时候无法获得其中的信息。

  该公司希望在保证一天的任意时候都能获得每条信息的情况下,将某些数据站的维护时间延后一小时。

  给出的数据保证在更改前能在一天的任意时候都能获得每条信息,问最少需要延后多少个(至少一个)。

 

思路:

  每个数据站都作为一个点,如果A、B有保存同一条信息并且A延后一小时会和B相同(即此时不能得到这条信息),A发生变化会影响B,则添加一条有向边(A,B)。

  对建成的图利用Tarjan算法来得到其中的强连通分量(互相影响的几个站),之后对于每个强连通分量,统计它的出度,若出度不为0则代表着这个强连通分量发生变化会影响该强连通分量以外的点。

  选出强连通分量点最少并且出度为0的强连通分量,即为答案。

 

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#define P pair<int,int>
#define ll long long
#define INF 100000000
#define M 1e9+7
#define MAX 500010
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
using namespace std;

vector<int> e[MAX];
int n, m, h, a, b, no_sc, tot;
int t[MAX];
int dfn[MAX], low[MAX], num[MAX], scc[MAX], out[MAX];
stack <int> s;

void tarjan(int u)
{
	dfn[u] = low[u] = ++tot;
	s.push(u);
	for (int i : e[u]) {
		if (dfn[i] == 0) {
			tarjan(i);
			low[u] = min(low[u], low[i]);
		}
		else if (scc[i] == 0) 
			low[u] = min(low[u], dfn[i]);		
	}
	if (dfn[u] == low[u]) {
		no_sc++;
		while (1) {
			int x = s.top(); s.pop();
			scc[x] = no_sc;
			num[no_sc]++;
			if (x == u)
				break;
		}
	}
}

int main()
{
	no_sc = 0; tot = 0;
	cin >> n >> m >> h;
	for (int i = 1; i <= n; i++)
		cin >> t[i];
	for (int i = 1; i <= m; i++) {
		cin >> a >> b;
		if ((t[a] + 1) % h == t[b]) 
			e[a].push_back(b);
		if ((t[b] + 1) % h == t[a])
			e[b].push_back(a);
	}

	for (int i = 1; i <= n; i++)
		if (dfn[i] == 0)
			tarjan(i);

	for (int i = 1; i <= n; i++) {
		for (int j : e[i]) {
			if (scc[i] != scc[j])
				out[scc[i]]++;
		}
	}

	int ans = 100010, flag;
	for (int i = 1; i <= no_sc; i++) {
		if (num[i] < ans&&out[i] == 0) {
			ans = num[i];
			flag = i;
		}
	}

	cout << ans << endl;
	for (int i = 1; i <= n; i++)
		if (scc[i] == flag)
			cout << i << ' ';
	cout << endl;

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值