苏塞克王国是世界上创新技术的领先国家,在王国中有n个城市,标记为1到n。
由于小K的研究,我们最终能过在两个城市之间建立传输管道,一个传输管道能单向连接两个城市,即,一个从城市x到城市y的传输管道不能被用于从城市y传输到城市x。在每个城市之间的运输系统已经建立完善,因此,如果从城市x到城市y的管道和从城市y到城市z的管道都被已经被建立,人们能够立即从x到z。
小K也研究了国家政治,他认为在这m对城市(ai, bi) (1 ≤ i ≤ m)之间的传输尤其重要。他正在计划为每个重要城市对(ai, bi)建立传输管道,通过使用一个或多个传输管道,我们可以从城市ai到城市bi(但不需要从城市bi到城市ai)。我们要找出必须建立的传输管道的最小数。至今,还没有传输管道被建立,在每个城市之间也没有其他有效的传输方式。
对于第一个样例,其中一条最优路径如下图:

第一行是两个以空格隔开的整数n和m(2 ≤ n ≤10^5 , 1 ≤ m ≤ 10^5 ),分别表示在苏塞克王国中的城市数和重要城市对的数。 之后m行描述重要城市对,第i行 (1 ≤ i ≤ m)包含两个以空格隔开的整数ai和bi(1 ≤ ai, bi ≤ n, ai ≠ bi),表示必须能通过一条或两条传输管道从城市ai到城市bi(但不需要从城市bi到城市ai),我们保证所有的城市对(ai, bi)是唯一的。
输出满足小K目的所需要的传输管道的最小数。
4 5 1 2 1 3 1 4 2 3 2 4
3
思路(非常综合的一个题):
1、如果有环存在的强连通分量,对应其中有n个点,那么如果我们单单想要分配给这个强连通分量尽可能少的管道数,很明显就是n,但是如果我们此时有这样的情况(两个强连通分量有一条边需要将其连接的情况):
‘
那么很明显,这条绿线是多余的,我们只要构造出来的图是一个大环,那么一共只需要花费6个管道即可。
那么结论:如果一个(弱)连通图中,包含n个顶点,包含强连通分量,那么对应整个图的建立需要n个管道。
如果不包含强连通分量,那么对应整个图的建立只需要n-1个管道。
2、那么我们首先跑一遍强连通Tarjan,将其全部染色并且缩点.再之后我们用并查集处理每一块连通图(弱连通图),对应发现其中如果有强连通分量存在的话,那么此连通图需要n个管道,否则就需要n-1个管道,累加统计即可。
3、细节比较多,而且代码实现不是很容易,大家耐心些预祝1A.~
Ac代码:
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
int n,m;
vector<int >mp[2000600];
int f[2000600];
int color[2000600];
int contz[2000600];
int flag[2000600];
int dfn[200600];
int low[200600];
int vis[200600];
int stack[200600];
int sig,cnt,tt;
int find(int a)
{
int r=a;
while(f[r]!=r)
r=f[r];
int i=a;
int j;
while(i!=r)
{
j=f[i];
f[i]=r;
i=j;
}
return r;
}
void merge(int a,int b)
{
int A,B;
A=find(a);
B=find(b);
if(A!=B)
{
f[B]=A;
if(contz[a]>1||contz[b]>1)
flag[A]=1;
flag[A]+=flag[B];
}
}
void Tarjan(int u)
{
stack[++tt]=u;
dfn[u]=low[u]=cnt++;
vis[u]=1;
for(int i=0;i<mp[u].size();i++)
{
int v=mp[u][i];
if(vis[v]==0)
{
Tarjan(v);
low[u]=min(low[u],low[v]);
}
if(vis[v]==1)low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u])
{
sig++;
do
{
color[stack[tt]]=sig;
vis[stack[tt]]=-1;
}
while(stack[tt--]!=u);
}
}
void Slove()
{
tt=-1;cnt=1;sig=0;
memset(contz,0,sizeof(contz));
memset(color,0,sizeof(color));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
memset(stack,0,sizeof(stack));
for(int i=1;i<=n;i++)
{
if(vis[i]==0)Tarjan(i);
}
int output=0;
for(int i=1;i<=sig;i++)f[i]=i,flag[i]=0;
for(int i=1;i<=n;i++)contz[color[i]]++;
for(int i=1;i<=sig;i++)
{
if(contz[i]>1)flag[i]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<mp[i].size();j++)
{
int u=color[i];
int v=color[mp[i][j]];
if(u!=v)
{
if(find(u)!=find(v))
{
merge(u,v);
}
}
}
}
for(int i=1;i<=sig;i++)
{
find(i);
if(f[i]==i)
{
if(flag[i]==0)output--;
}
output+=contz[i];
}
printf("%d\n",output);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)mp[i].clear();
for(int i=0;i<m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
mp[x].push_back(y);
}
Slove();
}
}