轰炸---------------tarjan
题目描述
有 n 座城市,城市之间建立了 m 条有向的地下通道。你需要发起若干轮轰炸,每轮可以轰炸任意多个城市。但每次轰炸的城市中,不能存在两个不同的城市 i,j 满足可以通过地道从城市 i 到达城市 j。你需要求出最少需要多少轮可以对每座城市都进行至少一次轰炸。
输入数据
第一行两个整数 n,m。接下来 m 行每行两个整数 a,b 表示一条从 a 连向 b 的单向边。
输出数据
一行一个整数表示答案。
样例输入
5 4
1 2
2 3
3 1
4 5
样例输出
3
数据范围
对于 20%的数据,n,m≤10。
对于 40%的数据,n,m≤1000。
对于另外 30%的数据,保证无环。
对于 100%的数据,n,m≤1000000。
一句话题解:偏序集最小反链覆盖等于最长链。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,x,y,ans; 4 int cnt,top,sum; 5 int dfn[1000050]; 6 int low[1000050]; 7 int col[1000050]; 8 int siz[1000050]; 9 int du[1000050]; 10 int t[1000050]; 11 int f[1000050]; 12 struct node 13 { 14 int tot; 15 int head[1000050]; 16 int nex[1000050]; 17 int ver[1000050]; 18 void add(int x,int y) 19 { 20 nex[++tot]=head[x]; 21 ver[tot]=y; 22 head[x]=tot; 23 } 24 }a,b; 25 void tarjan(int u) 26 { 27 dfn[u]=low[u]=++cnt; 28 t[++top]=u; 29 for(int i=a.head[u];i;i=a.nex[i]) 30 if(!dfn[a.ver[i]]) 31 { 32 tarjan(a.ver[i]); 33 low[u]=min(low[u],low[a.ver[i]]); 34 } 35 else if(!col[a.ver[i]]) 36 low[u]=min(low[u],dfn[a.ver[i]]); 37 if(dfn[u]==low[u]) 38 { 39 ++sum; 40 while(t[top+1]!=u) 41 { 42 col[t[top]]=sum; 43 ++siz[sum]; 44 --top; 45 } 46 } 47 } 48 int main() 49 { 50 scanf("%d%d",&n,&m); 51 for(int i=1;i<=m;++i) 52 { 53 scanf("%d%d",&x,&y); 54 a.add(x,y); 55 } 56 for(int i=1;i<=n;++i) 57 if(!dfn[i]) 58 tarjan(i); 59 for(int i=1;i<=n;++i) 60 for(int j=a.head[i];j;j=a.nex[j]) 61 if(col[i]!=col[a.ver[j]]) 62 { 63 ++du[col[a.ver[j]]]; 64 b.add(col[i],col[a.ver[j]]); 65 } 66 top=0; 67 for(int i=1;i<=sum;++i) 68 if(du[i]==0) 69 t[++top]=i; 70 while(top) 71 { 72 int now=t[top]; 73 f[now]+=siz[now]; 74 --top; ans=max(ans,f[now]); 75 for(int i=b.head[now];i;i=b.nex[i]) 76 { 77 f[b.ver[i]]=max(f[b.ver[i]],f[now]); 78 if(--du[b.ver[i]]==0) 79 t[++top]=b.ver[i]; 80 } 81 } 82 printf("%d",ans); 83 return 0; 84 }