Codeforces 949C Data Center Maintenance 强连通分量缩点

C. Data Center Maintenance
time limit per test
1 second
memory limit per test
512 megabytes
input
standard input
output
standard output

BigData Inc. is a corporation that has n data centers indexed from 1 to n that are located all over the world. These data centers provide storage for client data (you can figure out that client data is really big!).

Main feature of services offered by BigData Inc. is the access availability guarantee even under the circumstances of any data center having an outage. Such a guarantee is ensured by using the two-way replication. Two-way replication is such an approach for data storage that any piece of data is represented by two identical copies that are stored in two different data centers.

For each of m company clients, let us denote indices of two different data centers storing this client data as ci, 1 and ci, 2.

In order to keep data centers operational and safe, the software running on data center computers is being updated regularly. Release cycle of BigData Inc. is one day meaning that the new version of software is being deployed to the data center computers each day.

Data center software update is a non-trivial long process, that is why there is a special hour-long time frame that is dedicated for data center maintenance. During the maintenance period, data center computers are installing software updates, and thus they may be unavailable. Consider the day to be exactly h hours long. For each data center there is an integer uj (0 ≤ uj ≤ h - 1) defining the index of an hour of day, such that during this hour data center j is unavailable due to maintenance.

Summing up everything above, the condition uci, 1 ≠ uci, 2 should hold for each client, or otherwise his data may be unaccessible while data centers that store it are under maintenance.

Due to occasional timezone change in different cities all over the world, the maintenance time in some of the data centers may change by one hour sometimes. Company should be prepared for such situation, that is why they decided to conduct an experiment, choosing some non-empty subset of data centers, and shifting the maintenance time for them by an hour later (i.e. if uj = h - 1, then the new maintenance hour would become 0, otherwise it would become uj + 1). Nonetheless, such an experiment should not break the accessibility guarantees, meaning that data of any client should be still available during any hour of a day after the data center maintenance times are changed.

Such an experiment would provide useful insights, but changing update time is quite an expensive procedure, that is why the company asked you to find out the minimum number of data centers that have to be included in an experiment in order to keep the data accessibility guarantees.

Input

The first line of input contains three integers nm and h (2 ≤ n ≤ 100 0001 ≤ m ≤ 100 0002 ≤ h ≤ 100 000), the number of company data centers, number of clients and the day length of day measured in hours.

The second line of input contains n integers u1, u2, ..., un (0 ≤ uj < h), j-th of these numbers is an index of a maintenance hour for data center j.

Each of the next m lines contains two integers ci, 1 and ci, 2 (1 ≤ ci, 1, ci, 2 ≤ nci, 1 ≠ ci, 2), defining the data center indices containing the data of client i.

It is guaranteed that the given maintenance schedule allows each client to access at least one copy of his data at any moment of day.

Output

In the first line print the minimum possible number of data centers k (1 ≤ k ≤ n) that have to be included in an experiment in order to keep the data available for any client.

In the second line print k distinct integers x1, x2, ..., xk (1 ≤ xi ≤ n), the indices of data centers whose maintenance time will be shifted by one hour later. Data center indices may be printed in any order.

If there are several possible answers, it is allowed to print any of them. It is guaranteed that at there is at least one valid choice of data centers.

Examples
input
Copy
3 3 5
4 4 0
1 3
3 2
3 1
output
Copy
1
3 
input
Copy
4 5 4
2 1 0 3
4 3
3 2
1 2
1 4
1 3
output
Copy
4
1 2 3 4 
Note

Consider the first sample test. The given answer is the only way to conduct an experiment involving the only data center. In such a scenario the third data center has a maintenance during the hour 1, and no two data centers storing the information of the same client have maintenance at the same hour.

On the other hand, for example, if we shift the maintenance time on hour later for the first data center, then the data of clients 1 and 3 will be unavailable during the hour 0.


首先从最简单的情况看起。如果一个数据库延后一小时后其工作时间与另一个数据库相等,且这两个数据库中保存有同一份数据的两个备份,则将前者作为起点,而后者作为终点,连一条单向边。建图后延后每一点延后工作时间将导致此点能到达的点全部都得延后工作时间。假设此图中没有环,每一点的价值为延后这一点将导致的必须延后的点的总个数。题目要求这个价值最小的点。对于最简单情况中的每一点,这点的价值是与其直接相连的、其能到达的点的价值和加一。

现在我们假设图中有环。有环使得该有向图产生强连通分量。对于一个强连通分量中的任何一点,其价值都等于这个强连通分量所包含点的个数。于是使用tarjan算法求强连通分量,缩点,每点的价值等于原强连通分量所包含点的个数。

对于进行过缩点操作后的单向图,我们寻求答案的思路和最简单的情况是一样的。因此,我们应该在新图中所有没有出度的点当中寻找一个价值最小的点,并输出作为答案。

由于我平时不学无术,今天做这个题一共WA了八次,在最开始建图进行连边时,一定要对保存同一份数据的两个点进行两次判断,否则在TEST4里一天2小时、两个数据库分别在0时和1时工作的情况下,代码只会连一条边而不是两条。

由于我平时不好好学习,缩点时用了一个愚蠢的方法,就是将vis数组标为-1,0,1,标为-1的点在缩点所进行的dfs的过程中从未被访问过,标为0的点曾被其他连通分量访问过,1正在被当前的连通分量访问。然后就超时了,啊,活该!

结果在改正上面缩点的算法时,突然变笨了,将标为-1和0的点合并了,这当然会WA,因为除了第一个访问的连通分量外,别的连通分量一旦有一部分与已经枚举过的连通分量有重合部分,必定是会直接跳过这一部分的。

最后用链表把所有的边都访问了一遍,既省时,又好写,这才是聪明人应该立刻想到的写法。

贴一份已A的代码:

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;

int n,m,h;
vector<int> vec;
const int maxn=100010;
int dfn[maxn];
int low[maxn];
int vis[maxn];
int num[maxn];
int time[maxn];
int belong[maxn];
int  cnt;
int tot;
struct Edge
{
    int v;
    int next;
}edge[2*maxn];
int edgecount;
int head[maxn];
void Init()
{
    edgecount=0;
    memset(head,-1,sizeof(head));
}
void Add_edge(int u,int v)
{
    edge[++edgecount].v=v;
    edge[edgecount].next=head[u];
    head[u]=edgecount;
}
stack<int> st;
int outdegree[maxn];

void tarjan(int u)
{
    dfn[u]=low[u]=++tot;
    vis[u]=1;
    st.push(u);
    for (int k=head[u]; k!=-1; k=edge[k].next)
    {
        int v=edge[k].v;
        if (!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if (vis[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if (low[u]==dfn[u])
    {
        int x;
        ++cnt;
        int sum=0;
        while (1)
        {
            x=st.top();
            st.pop();
            vis[x]=0;
            belong[x]=cnt;
            sum++;
            if (x==u) break;
        }
        num[cnt]=sum;
    }
}

void solve()
{
    tot=0;
    cnt=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    while(!st.empty()) st.pop();
    for (int i=1; i<=n; i++)
    {
        if (dfn[i]==0) tarjan(i);
    }
    memset(vis,0,sizeof(vis));
    memset(outdegree,0,sizeof(outdegree));
    for (int i=1; i<=n; i++)
    {
        for (int j=head[i]; j!=-1; j=edge[j].next)
        {
            int v=edge[j].v;
            int p=belong[i];
            int q=belong[v];
            if (p!=q)
            {
                outdegree[p]++;
            }
        }
    }
    int fn=100010; int fnj=0;
    for (int i=1; i<=cnt; i++)
    {
        if (outdegree[i]==0)
        {
            if (fn>num[i])
            {
                fn=num[i];
                fnj=i;
            }
        }
    }
    printf("%d\n",fn);
    for (int i=1; i<=n; i++)
    {
        if (belong[i]==fnj)
        {
            vec.push_back(i);
        }
    }
    for (int i=1; i<vec.size(); i++)
    {
        printf("%d ",vec[i-1]);
    }
    printf("%d\n",vec[vec.size()-1]);
}

int main()
{
    Init();
    scanf("%d%d%d",&n,&m,&h);
    for (int i=1; i<=n; i++)
    {
        scanf("%d",&time[i]);
    }
    for (int i=1; i<=m; i++)
    {
        int p,q;
        scanf("%d%d",&p,&q);
        if ((time[p]+1)%h==time[q])
        {
            Add_edge(p,q);
        }
        if ( (time[q]+1)%h==time[p] )
        {
            Add_edge(q,p);
        }
    }
    solve();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值