Codeforces Round #469(Div.1 C) (Div. 2 E)Data Center Maintenance(tarjan缩点)

Problem

  给你n个数据中心,m个客户,并告诉你一天的小时数h(2 ≤ n ≤ 100 000, 1 ≤ m ≤ 100 000, 2 ≤ h ≤ 100 000)。
  对于数据中心j,它在 u j 这个小时内会进行为期1小时的维修。
  对于客户i,他的数据存储在 c i , 1 c i , 2 这两个数据中心里。你不能使这两个中心同时维修,也就是不能使 u c i , 1 = u c i , 2 ,不然那个客户就会gg。
  现在,你要使至少一个数据中心的维修时间推迟1个小时,就是使 u j = ( u j + 1 ) % h 。求最少要使多少个数据中心推迟1小时,以及一种方案。

Solution

  考虑转换模型。
  若数据中心x被推迟,中心y会因此而不得不推迟,就从x向y连一条边。比如h=5, u 1 = 4 u 3 = 0 ,使中心1推迟1小时就会让 u 1 变成0。如果有个客户要使用中心1、3,那么就得从1向3连条边。
  这样,问题就被转化成求每个点能到达哪些点,并取最小值。


  n ≤ 100 000,不能暴力枚举起点再洪水灌溉,强行求解每个点能到哪些点。
  那么可以考虑tarjan。


  首先,我们使用tarjan将所有强联通分量缩为一个个点。如果我们选了某个分量中任意一点,那么这整个分量都会被选。
  于是,题目被转化为求每个分量能到达多少个点,并取最小值。可以拓扑排序+DP强行计算。
  考虑两个分量A、B,设A有一条连向B的边。
  如果我们选了分量A,那么分量B也得被选。这显然不是最优的。
  换句话说说,选了某个分量,该分量连向的分量也得被选。
  于是,我们可以只考虑出度为0的分量。选取这些分量中size最小的一个。


  时间复杂度: O ( n + m )

Code

#include <cstdio>
#include <vector> 
#define min(a,b) ((a)<(b)?(a):(b))
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;

const int N=1e5+1,M=N<<1,inf=0x7FFFFFFF;
int i,n,m,h,u[N],c1,c2,tot,final[N],ans,st;
struct side
{
    int to,ne;
}e[M];

inline void link(int x,int y)
{
    e[++tot].to=y; e[tot].ne=final[x]; final[x]=tot;
}

int t,dfn[N],low[N],sta[N],top,col[N],cn,x,y,sz[N],s;
bool vis[N],out[N];
vector<int>cir[N];
vector<int>::iterator it;
void tarjan(int x)
{
    dfn[x]=low[x]=++t; vis[x]=1; sta[++top]=x;
    int i,y;
    for(i=final[x];i;i=e[i].ne)
    {
        y=e[i].to;
        if(!dfn[y]) tarjan(y), low[x]=min(low[x],low[y]);
        else    
        if(vis[y])  low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x])
    {
        vis[x]=0; col[x]=++cn; sz[cn]=1; cir[cn].push_back(x);
        while(sta[top]!=x) 
        {
            col[sta[top]]=cn;
            vis[sta[top]]=0;
            sz[cn]++;
            cir[cn].push_back(sta[top--]);
        }
        top--;  
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&h);
    fo(i,1,n) scanf("%d",&u[i]);
    fo(i,1,m) 
    {
        scanf("%d%d",&c1,&c2);
        if((u[c1]+1)%h==u[c2]) link(c1,c2);
        if((u[c2]+1)%h==u[c1]) link(c2,c1);
    }

    fo(i,1,n) if(!dfn[i]) tarjan(i);

    tot=0;
    fo(x,1,n)
        for(i=final[x];i;i=e[i].ne) 
        {
            y=e[i].to;
            if(col[x]!=col[y]) out[col[x]]=1;
        }

    s=inf;  
    fo(i,1,cn) if(!out[i]&&sz[i]<s) s=sz[i], x=i;   
    printf("%d\n",s);
    for(it=cir[x].begin();it!=cir[x].end();it++) printf("%d ",*it);
}

猜你喜欢

转载自blog.csdn.net/qq_36551189/article/details/80928472