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;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Rubbishes/p/8969196.html
今日推荐