Codeforces 950E Data Center Maintenance ( 思维 && 强连通分量缩点 )
题意 : 给出 n 个点,每个点有一个维护时间 a[i]。m 个条件,每个条件有2个点(x,y)且 a[x] != a[y]。选择最少的 k (最少一个)个点,使其值加1后,m个条件仍成立。
分析 :
发现改变某些数加一后可能产生联动效应
换句话说就是改变某些数则必须改变另一些数来维持 m 个条件的成立
这个可以用图来表示,对于给出来的每一个 (x, y) 如果改变 x 后等于 y 则连 x => y 边
表示要改变 x 则必须改变 y,然后对于 y 进行同样的判断是否连边
最后建完图后,若有成环的,则这个环上的点要加一改变,则环上所有点都必须进行改变
因此将整个图进行强连通分量缩点,最后考察所有出度为 0 的的分量,哪个分量点最少
则这个点集就是答案,出度不为 0 的点肯定不是最优的,因为这些点定会回到出度为 0 的点
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; const int maxm = 2e5 + 10; struct EDGE{ int v, nxt; }Edge[maxm]; int Head[maxn], cnt; int DFN[maxn], LOW[maxn], color[maxn], INDEX, id; bool vis[maxn], out[maxn]; stack<int> stk; vector<int> num[maxn]; int N, M, H; inline void init() { while(!stk.empty()) stk.pop(); for(int i=0; i<=N; i++){ Head[i] = DFN[i] = LOW[i] = color[i] = -1; out[i] = false; num[i].clear(); }cnt = INDEX = id = 0; } inline void AddEdge(int from, int to) { Edge[cnt].v = to; Edge[cnt].nxt = Head[from]; Head[from] = cnt++; } inline void tarjan(int u) { DFN[u] = LOW[u] = INDEX++; stk.push(u); vis[u] = true; for(int i=Head[u]; i!=-1; i=Edge[i].nxt){ int Eiv = Edge[i].v; if(DFN[Eiv] == -1){ tarjan(Eiv); LOW[u] = min(LOW[u], LOW[Eiv]); }else{ if(vis[Eiv]) LOW[u] = min(LOW[u], LOW[Eiv]); } } if(DFN[u] == LOW[u]){ color[u] = ++id; num[id].push_back(u);; vis[u] = false; while(stk.top() != u){ vis[stk.top()] = false; color[stk.top()] = id; num[id].push_back(stk.top()); stk.pop(); } stk.pop(); } } int arr[maxn]; int main(void) { scanf("%d %d %d", &N, &M, &H); init(); for(int i=1; i<=N; i++) scanf("%d", &arr[i]); int u, v; while(M--){ scanf("%d %d", &u, &v); if((arr[u]+1)%H == arr[v]) AddEdge(u, v); if((arr[v]+1)%H == arr[u]) AddEdge(v, u); } for(int i=1; i<=N; i++) if(DFN[i] == -1) tarjan(i);///强连通分量缩点 for(int i=1; i<=id; i++){///统计缩点后每个点的出入度情况 for(int j=0; j<num[i].size(); j++){ u = num[i][j]; for(int k=Head[u]; k!=-1; k=Edge[k].nxt){ int Eiv = Edge[k].v; if(color[Eiv] != i){ out[i] = true; break; } } if(out[i]) break; } } int MM = 0x3f3f3f3f, which; for(int i=1; i<=id; i++){ if(!out[i] && num[i].size() < MM){ MM = num[i].size(); which = i; } } printf("%d\n", MM); for(int i=1; i<=N; i++) if(color[i] == which) printf("%d ", i); puts(""); return 0; }