题目大意:$n$个点,每个点有一个值$w_i$。$m$个条件,每个条件给出$x,y$,要求$w_x ot =w_y$。选择最少的点,使其值加$1$后,所有条件成立(数据保证有解)。
题解:对于每个条件,若$(w_x+1)mod h=w_y$,连上$x->y$;若$(w_y+1)mod h=w_x$,连上$y->x$。一条边的含义是,若起点加一,终点也要加一。缩点,强连通分量内的点要一起加。发现答案就是找最小的没有出边的点
卡点:无
C++ Code:
#include <cstdio> #define maxn 100010 #define maxm maxn #define gethour(x) ((x + 1) % h) int n, m, h; int w[maxn]; int head[maxn << 1], cnt; struct Edge { int from, to, nxt; } e[maxm << 2]; inline void addE(int a, int b) { e[++cnt] = (Edge) {a, b, head[a]}; head[a] = cnt; } int DFN[maxn], low[maxn], idx, sz[maxn]; int S[maxn], top, res[maxn], CNT; bool ins[maxn]; inline int min(int a, int b) {return a < b ? a : b;} void tarjan(int u) { DFN[u] = low[u] = ++idx; ins[S[++top] = u] = true; int v; for (int i = head[u]; i; i = e[i].nxt) { v = e[i].to; if (!DFN[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (ins[v]) low[u] = min(low[u], DFN[v]); } if (DFN[u] == low[u]) { CNT++; do { ins[v = S[top--]] = false; sz[res[v] = CNT]++; addE(CNT + n, v); } while (u != v); } } int oud[maxn]; int main() { scanf("%d%d%d", &n, &m, &h); for (int i = 1; i <= n; i++) scanf("%d", w + i); for (int i = 0, a, b; i < m; i++) { scanf("%d%d", &a, &b); if (w[a] == gethour(w[b])) addE(b, a); if (w[b] == gethour(w[a])) addE(a, b); } int cnt_now = cnt; for (int i = 1; i <= n; i++) if (!DFN[i]) tarjan(i); for (int i = 1; i <= cnt_now; i++) { int u = e[i].from, v = e[i].to; if (res[u] != res[v]) oud[res[u]]++; } int ans = 0x3f3f3f3f, mini = n + 1; for (int i = 1; i <= CNT; i++) if (!oud[i] && ans > sz[i]) { ans = sz[i]; mini = i + n; } printf("%d ", ans); for (int i = head[mini]; i; i = e[i].nxt) printf("%d ", e[i].to); puts(""); return 0; }