题意
- 给你\(n\)个点,每个点有一个权值\(a_x\),有\(m\)个限制条件形如\(a_x≠a_y\),现在要求你选出\(k(k>0)\)个点使其权值在模\(h\)意义下加\(1\),问最少选多少个点能让限制条件继续满足。
\(Tarjan\)求强连通分量模板题
发现对于每个限制条件,如果\(a_x+1=a_y(mod\ h)\)
那么可以给\(y\)向\(x\)连一条有向边
那么最后缩点后找到入度为\(0\)的最小\(scc\)大小即可
Codes
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int colors, n, m, h, a[N], be[N], size[N], de[N];
namespace Tarjan {
int from[N << 1], to[N << 1], nxt[N << 1], head[N], e;
int ins[N], Sta[N], low[N], dfn[N], dfn_cnt, top;
inline void add(int x, int y) {
to[++ e] = y; nxt[e] = head[x]; head[x] = e;
}
inline void dfs(int x) {
low[x] = dfn[x] = ++ dfn_cnt, ins[Sta[++ top] = x] = 1;
for (int i = head[x]; i; i = nxt[i]) {
if (!dfn[to[i]]) {
dfs(to[i]);
low[x] = min(low[to[i]], low[x]);
}
else if (ins[to[i]])
low[x] = min(dfn[to[i]], low[x]);
}
if (low[x] == dfn[x]) {
++ colors;
do {
++ size[be[Sta[top]] = colors];
ins[Sta[top]] = 0;
}while (Sta[top --] ^ x);
}
}
inline void Get_Ans() {
for (int x = 1; x <= n; ++ x)
for (int i = head[x]; i; i = nxt[i])
if (be[x] ^ be[to[i]])
++ de[be[to[i]]];
int ans = 1e9, id;
for (int i = 1; i <= colors; ++ i)
if (!de[i] && size[i] < ans)
ans = size[i], id = i;
printf("%d\n", ans);
for (int i = 1; i <= n; ++ i)
if (be[i] == id)
printf("%d ", i);
}
}
int main() {
#ifdef ylsakioi
freopen("cf949c.in", "r", stdin);
freopen("cf949c.out", "w", stdout);
#endif
scanf("%d%d%d", &n, &m, &h);
for (int i = 1; i <= n; ++ i)
scanf("%d", &a[i]);
for (int x, y, i = 1; i <= m; ++ i) {
scanf("%d%d", &x, &y);
if ((a[x] + 1) % h == a[y]) Tarjan::add(y, x);
if ((a[y] + 1) % h == a[x]) Tarjan::add(x, y);
}
for (int i = 1; i <= n; ++ i)
if (!Tarjan::dfn[i])
Tarjan::dfs(i);
Tarjan::Get_Ans();
return 0;
}
原文地址:https://www.cnblogs.com/brunch/p/9909343.html
时间: 2024-10-31 04:10:40