JZOJ 5452. 【NOIP2017提高A组冲刺11.5】轰炸

Description

战狂也在玩《魔方王国》。他只会征兵而不会建城市,因此他决定对小奇的城市进行轰炸。
小奇有n 座城市,城市之间建立了m 条有向的地下通道。战狂会发起若干轮轰炸,每轮可以轰炸任意多个城市。
每座城市里都有战狂部署的间谍,在城市遭遇轰炸时,它们会通过地下通道撤离至其它城市。非常不幸的是,在地道里无法得知其它城市是否被轰炸,如果存在两个不同的城市i,j,它们在同一轮被轰炸,并且可以通过地道从城市i 到达城市j,那么城市i 的间谍可能因为撤离到城市j 而被炸死。为了避免这一情况,战狂不会在同一轮轰炸城市i 和城市j。
你需要求出战狂最少需要多少轮可以对每座城市都进行至少一次轰炸。

Input

第一行两个整数n,m。接下来m 行每行两个整数a,b 表示一条从a 连向b的单向边。

Output

输出一行仅一个整数表示答案。

Sample Input

5 4
1 2
2 3
3 1
4 5

Sample Output

3

Data Constraint

对于20%的数据,n,m<=10。
对于40%的数据,n,m<=1000。
对于另外30%的数据,保证无环。
100%的数据,n,m<=1000000。

Solution

  • 这题的描述真的不清楚啊!“到达”指的不是相邻边,而是“能通过一些边到达”。

  • 那么显然一个强连通分量中所需的步数就是其总点数!

  • 于是我们先用 tarjan 算法 缩环为点 ,这个点的权值就是其点数。

  • 这样原图就变成一个 DAG ,为了知道最小轮数,则跑一遍最长路即可.

  • 因为肯定是分层地跑最优,于是用拓扑排序的方式实现即可(注意点权为强联通分量点数)。

  • 而且由于 N 比较大( 106 ) , tarjan 算法需要用人工栈。

  • 时间复杂度 O(N)

Code

#include
using namespace std;
const int N=1e6+1;
int tot,top,num,ans;
int first[N],next[N],en[N];
int first1[N],next1[N],en1[N];
int dfn[N],low[N],stack[N],MS[N],r[N];
int f[N],g[N],d[N],q[N],bel[N];
bool bz[N],vis[N];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline int max(int x,int y)
{
    return x>y?x:y;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
inline void insert(int x,int y)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
inline void insert1(int x,int y)
{
    next1[++tot]=first1[x];
    first1[x]=tot;
    en1[tot]=y;
    d[y]++;
}
inline void tarjan(int x)
{
    MS[MS[0]=1]=x;
    while(MS[0])
    {
        if(!dfn[x=MS[MS[0]]])
        {
            dfn[x]=low[x]=++tot;
            r[stack[++top]=x]=first[x];
            bz[x]=vis[x]=true;
        }
        bool enter=false;
        for(int i=r[x];i;i=next[i])
            if(!vis[en[i]])
            {
                r[x]=next[i];
                MS[++MS[0]]=en[i];
                enter=true;
                break;
            }else
                if(bz[en[i]]) low[x]=min(low[x],dfn[en[i]]);
        if(enter) continue;
        if(dfn[x]==low[x])
        {
            num++;
            do
            {
                bel[stack[top]]=num;
                bz[stack[top--]]=false;
            }while(stack[top+1]!=x);
        }
        low[MS[--MS[0]]]=min(low[MS[MS[0]]],low[x]);
    }
}
int main()
{
    int n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        insert(x,y);
    }
    tot=0;
    for(int i=1;i<=n;i++)
        if(!vis[i]) tarjan(i);
    tot=0;
    for(int i=1;i<=n;i++)
    {
        g[bel[i]]++;
        for(int j=first[i];j;j=next[j])
            if(bel[i]^bel[en[j]]) insert1(bel[i],bel[en[j]]);
    }
    int l=0,r=0;
    for(int i=1;i<=n;i++)
        if(!d[i]) f[q[++r]=i]=g[i];
    while(lint x=q[++l];
        if(!first1[x]) ans=max(ans,f[x]);
        for(int i=first1[x];i;i=next1[i])
        {
            f[en1[i]]=max(f[en1[i]],f[x]+g[en1[i]]);
            if(!--d[en1[i]]) q[++r]=en1[i];
        }
    }
    printf("%d",ans);
    return 0;
}

你可能感兴趣的:(Tarjan,最长路,拓扑排序,图论,人工栈,图论,最长路,Tarjan,拓扑排序,人工栈)