1. 程式人生 > >BZOJ5450: 轟炸(水題,Tarjan縮點求最長路)

BZOJ5450: 轟炸(水題,Tarjan縮點求最長路)

5450: 轟炸

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 43  Solved:18
[Submit][Status][Discuss]

Description

有n座城市,城市之間建立了m條有向的地下通道。你需要發起若干輪轟炸,每輪可以轟炸任意多個城市。但每次轟 炸的城市中,不能存在兩個不同的城市i,j滿足可以通過地道從城市i到達城市j。你需要求出最少需要多少輪可以 對每座城市都進行至少一次轟炸。

Input

第一行兩個整數n,m。接下來m行每行兩個整數a,b表示一條從a連向b的單向邊。 n,m<=1000000。

Output

一行一個整數表示答案。

Sample Input

5 4
1 2
2 3
3 1
4 5

Sample Output

3

 

思路:不同的鏈不會相互影響,所以可以同時炸,顯然時間只取決於最長的有向鏈。我們縮點,然後找最長路即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1000010;
int Laxt[maxn],Next[maxn],To[maxn],cnt,scc_cnt,head;
int instk[maxn],q[maxn],dfn[maxn],low[maxn],scc[maxn],times; vector<int>G[maxn]; int sz[maxn],dis[maxn],ind[maxn],ans; void read(int &x){ x=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } void add(int
u,int v){ Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } void tarjan(int u) { instk[u]=1; q[++head]=u; dfn[u]=low[u]=++times; for(int i=Laxt[u];i;i=Next[i]){ int v=To[i]; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(instk[v])low[u]=min(low[u],dfn[v]);//無向圖與有向圖的區別 } if(dfn[u]==low[u]){ scc_cnt++; while(true){ int x=q[head--]; scc[x]=scc_cnt; instk[x]=0; sz[scc_cnt]++; if(x==u) break; } } } int main() { int N,M,u,v; scanf("%d%d",&N,&M); rep(i,1,M) read(u),read(v),add(u,v); rep(i,1,N) if(!dfn[i]) tarjan(i); rep(i,1,N){ for(int j=Laxt[i];j;j=Next[j]){ if(scc[i]!=scc[To[j]]){ ind[scc[To[j]]]++; G[scc[i]].push_back(scc[To[j]]); } } } head=0; int tail=0; rep(i,1,scc_cnt) if(!ind[i]) q[++head]=i,dis[i]=sz[i]; while(tail<head){ int u=q[++tail]; ans=max(ans,dis[u]); int L=G[u].size(); for(int i=0;i<L;i++){ int v=G[u][i]; ind[v]--; dis[v]=max(dis[v],dis[u]+sz[v]); if(!ind[v]) q[++head]=v; } } printf("%d\n",ans); return 0; }