【TYVJ1936】【Clover杯NOIP模拟赛IV】太空战队

【问题描述】  

  在社会经济高速发展的今天,借助高科技手段,组建太空战队的愿望就快实现了。

  战队属下有N个航天员。作为空军选拔上来的佼佼者,每个航天员都有与生俱来的骄傲——他们每个人都认为自己是最强的或者是第二强的。这样,如何分组就成了司令官的难题了。司令官分组的方法是这样的:
  步骤1:任意选择一个未被分组的航天员,记为当前航天员.
  步骤2:把当前航天员分入一个新的组.
  步骤3:如果当前航天员心目中最强的那个航天员(记为Q航天员)还没有被分组,那么把Q航天员分入当前航天员所在组,并把Q航天员作为当前航天员并重复步骤3;如果Q航天员已经被分组,那么重复步骤1,直至所有航天员都被分入了某个组。
司令官想请你帮忙计算:按上面的分组方式,最多/最少能分成几个组。

  N<= 100000

【分析】

  这题比较简单。依照题给的边连成一个有向图(注意,不一定连通),然后强联通分量缩点染色,最多能分的组数就是缩点后的点数,最少能分的组就是缩点后入度为0的点数。

  注意自环。

  求强联通分量我用的是tarjan的算法,这个算法简洁,应用范围广,效率高,值得推荐。

【代码】

  

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int d[200000],low[200000],dfn[200000],edge[200000],color[200000],q[200000];
bool p[200000],instack[200000];
int n,Count,ct,qt;
void dfs(int x)
{
    p[x] = true;
    dfn[x] = low[x] = ++Count;
    q[++qt] = x;
    instack[x] = true;


    int v = edge[x];
    if (!p[v])
    {
        dfs(v);
        low[x] = min(low[x],low[v]);
    }else
    if (instack[v] && v != x)
        low[x] = min(low[x],dfn[v]);

    if (low[x] == dfn[x])
    {
        int j;
        ct ++;
        do 
        {
            j = q[qt];
            instack[j] = false;
            color[j] = ct;
            qt--;
        }while (j != x);
    }
}
int main()
{
    scanf("%d",&n);
    for (int i = 1;i <= n;i ++)
        scanf("%d",&edge[i]);
    for (int i = 1;i <= n;i ++)
        if (!p[i])
            dfs(i);
    int ans1 = ct;

    for (int i = 1;i <= n;i ++)
        if (color[i] != color[edge[i]])
            d[color[edge[i]]] ++;
    int ans2 = 0;
    for (int i = 1;i <= ct;i ++)
        if (!d[i])
            ans2 ++;
    printf("%d %d",ans1,ans2);
}

 

posted @ 2013-10-22 21:02  N_C_Derek  阅读(271)  评论(0编辑  收藏  举报