题面
共有 n 所学校 (1≤n≤10000) 已知他们实现设计好的网络共 m 条线路,为了保证高速,网络是单向的。现在请你告诉他们至少选几所学校作为共享软件的母机,能使每所学校都可以用上。再告诉他们至少要添加几条线路能使任意一所学校作为母机都可以使别的学校使用上软件。
显然是一道强连通分量(因为是有向图)
先用链式向前星建图,然后 Tarjan 缩点
看看哪些学校内部可以共享软件
初步思路
统计有几个拓扑序入度为0的强连通分量,即为作为共享软件的母机数
我最初的错误思路:有几个拓扑序出度为0的强连通分量,即至少要添加几条线路(每次将出度为0的点往入度为0的点连)
(但是挂的很离谱,因为……你会看到的,具体可以看查缺补漏那里)
#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define repe(i,u) for(int i = head[u];i;i = e[i].next)
const int N = 1e4 + 5;
struct edge{
int v,next;
}e[N*N];
int dfn[N],low[N],stac[N],head[N],bel[N],rudu[N],chudu[N];
int n,x,tot,cnt,ans1,ans2,top,idx;
inline void add(int u,int v){
e[++tot] = (edge){v,head[u]};
head[u] = tot;
}
bool vis[N];
void tarjan(int u){
low[u] = dfn[u] = ++cnt;
stac[++top] = u;
vis[u] = true;
repe(i,u)
if(!dfn[e[i].v]){
tarjan(e[i].v);
low[u] = min(low[u],low[e[i].v]);
}
else if(vis[e[i].v])
low[u] = min(low[u],dfn[e[i].v]);
if(low[u] == dfn[u]){
int cur;
idx++;
do {
cur = stac[top--];
vis[cur] = false;
bel[cur] = idx;
} while(cur != u);
}
}
int main(){
……//输入
rep(i,n) if(!dfn[i]) tarjan(i);
rep(i,n) repe(j,i) if(bel[i] != bel[e[j].v])
rudu[bel[e[j].v]]++,chudu[bel[i]]++;
rep(i,idx) ans1 += rudu[i] == 0,ans2 += chudu[i] == 0;
printf("%d\n%d",ans1,ans2);
return 0;
}
代码交上去了,然后获得了——81分的低分。它 WA 了
查缺补漏
下载数据,发现这样一张图:

这是一个环(强连通分量)。出度为0的点虽然有,但是不需要加边呀
特判了有没有边连接的两端点所属的强连通分量不同,交上去72分……
//以上部分与之前一样
int main(){
……//输入
rep(i,n) if(!dfn[i]) tarjan(i);
rep(i,n) repe(j,i) if(bel[i] != bel[e[j].v])
rudu[bel[e[j].v]]++,chudu[bel[i]]++,ccnt = true;
rep(i,idx) ans1 += rudu[i] == 0,ans2 += chudu[i] == 0;
printf("%d\n%d",ans1,ccnt ? ans2 : 0);
return 0;
}
有张图长这样

所以没有考虑不连通的点,实在不应该呢
所以又特判了一个地方
int main(){
……//以上没有变化
rep(i,n) if(!dfn[i]) tarjan(i),num++;
rep(i,n) repe(j,i) if(bel[i] != bel[e[j].v])
rudu[bel[e[j].v]]++,chudu[bel[i]]++,ccnt++;
rep(i,idx) ans1 += rudu[i] == 0,ans2 += chudu[i] == 0;
printf("%d\n%d",ans1,ccnt == 0 && num == 1 ? 0 : ans2);
return 0;
}
已经很接近正确答案了!但是……90分
再看看~随手制造出一组 hack。

出度为0的点有2个,但是入度为0的点有3个……
要连至少3条边才能让它变成强连通分量啊!!!
其实这是最显然的一个错,对入度出度取 即可
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define repe(i,u) for(int i = head[u];i;i = e[i].next)
const int N = 1e4 + 5;
struct edge{
int v,next;
}e[N*N];
int dfn[N],low[N],stac[N],head[N],bel[N],rudu[N],chudu[N];
int n,x,tot,cnt,ans1,ans2,top,idx,ccnt,num;
inline void add(int u,int v){
e[++tot] = (edge){v,head[u]};
head[u] = tot;
}
bool vis[N];
void tarjan(int u){
low[u] = dfn[u] = ++cnt;
stac[++top] = u;
vis[u] = true;
repe(i,u)
if(!dfn[e[i].v]){
tarjan(e[i].v);
low[u] = min(low[u],low[e[i].v]);
}
else if(vis[e[i].v])
low[u] = min(low[u],dfn[e[i].v]);
if(low[u] == dfn[u]){
int cur;
idx++;
do {
cur = stac[top--];
vis[cur] = false;
bel[cur] = idx;
} while(cur != u);
}
}
int main(){
scanf("%d",&n);
rep(i,n){
scanf("%d",&x);
while(x){
add(i,x);
scanf("%d",&x);
}
}
rep(i,n) if(!dfn[i]) tarjan(i),num++;
rep(i,n) repe(j,i) if(bel[i] != bel[e[j].v])
rudu[bel[e[j].v]]++,chudu[bel[i]]++,ccnt++;
rep(i,idx) ans1 += rudu[i] == 0,ans2 += chudu[i] == 0;
printf("%d\n%d",ans1,ccnt == 0 && num == 1 ? 0 : max(ans1,ans2));
return 0;
}
总结
先用 tarjan 缩点。
第一问答案是新图上入度为 0 的点数。
第二问答案是:
1.若新图上只有一个点,0
2.否则为新图上入度为 0 的点数与出度为 0 的点数取
370

被折叠的 条评论
为什么被折叠?



