传送门: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;
}