LibreOJ #10093. 「一本通 3.5 练习 1」网络协议&Luogu P2746校园网Network of Schools P2812校园网络【Network of Schools加强版】
LibreOJ #10093. 「一本通 3.5 练习 1」网络协议
Luogu P2746 [USACO5.3]校园网Network of Schools
Luogu P2812 校园网络【[USACO]Network of Schools加强版】
关于强连通分量,请见上一篇题解
传送门
题目、题解和代码
/*【题目描述】
出自 IOI 1996
一些学校连接在一个计算机网络上。学校之间存在软件支援协议。每个学校都有它应支援的学校名单(学校 a 支援学校 b,并不表示学校 b 一定支援学校 a)。当某校获得一个新软件时,无论是直接得到还是网络得到,该校都应立即将这个软件通过网络传送给它应支援的学校。因此,一个新软件若想让所有连接在网络上的学校都能使用,只需将其提供给一些学校即可。
任务
请编一个程序,根据学校间支援协议(各个学校的支援名单),计算最少需要将一个新软件直接提供给多少个学校,才能使软件通过网络被传送到所有学校;
如果允许在原有支援协议上添加新的支援关系。则总可以形成一个新的协议,使得此时只需将一个新软件提供给任何一个学校,其他所有学校就都可以通过网络获得该软件。编程计算最少需要添加几条新的支援关系。
【输入】
第一行是一个正整数 n,表示与网络连接的学校总数。 随后 n 行分别表示每个学校要支援的学校,即:i+1 行表示第 i 号学校要支援的所有学校代号,最后 0 结束。
如果一个学校不支援任何其他学校,相应行则会有一个 0。一行中若有多个数字,数字之间以一个空格分隔。
【输出】
包含两行,第一行是一个正整数,表示任务 a 的解,第二行也是一个正整数,表示任务 b 的解。
【输入样例】
5
2 4 3 0
4 5 0
0
0
1 0
【输出样例】
1
2
【提示】
数据范围与提示:
2≤n≤100。
(Luogu P2812,题意相同,数据范围为5000000)
由于这题思路同上题完全相同,不再赘述。
不同点:求最少加几条边,使得整个图变成一个强连通分量。
如果整个图就是一个强连通分量,答案为0(需特判)。
否则,答案为max(入度为0的点的个数,出度为0的点的个数)*/
#include<iostream>
#include<vector>
using namespace std;
template<typename t>
inline int min(t& a,t& b)
{
return a<b?a:b;
}
/* template<typename _Tp, typename _Compare>
inline const _Tp&
max(const _Tp& __a, const _Tp& __b, _Compare __comp)
{
//return __comp(__a, __b) ? __b : __a;
if (__comp(__a, __b))
return __b;
return __a;
}((vector内)忘了写max代码,就从源文件中摘抄出来了)*/
vector<int>edge[5000001];
int dfn[5000001],low[5000001],sta[5000001],ty[5000001],idu[5000001],odu[5000001],num,top,typ,ans1,ans2;//ans1代表入度为0的点个数,ans2代表出度为0的点个数,idu为入度,odu为出度,其他详见前一篇题解
inline void tarjan(int x)//不解释
{
dfn[x]=low[x]=++num;
sta[++top]=x;
for(register vector<int>::iterator i=edge[x].begin();i!=edge[x].end();++i)
{
register int y=*i;
if(dfn[y])
{
if(!ty[y])
{
low[x]=min(low[x],dfn[y]);
}
}
else
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
}
if(low[x]==dfn[x])
{
ty[x]=++typ;
while(sta[top]!=x)
{
ty[sta[top]]=typ;
--top;
}
--top;
}
return;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
register int n,a;
cin>>n;
for(register int i=1;i<=n;++i)
{
cin>>a;
while(a)//读入数不为0
{
edge[i].push_back(a);
cin>>a;
}
}
for(register int i=1;i<=n;++i)
{
if(!dfn[i])
{
tarjan(i);//搜索
}
}
for(register int i=1;i<=n;++i)
{
for(register vector<int>::iterator j=edge[i].begin();j!=edge[i].end();++j)
{
int x=*j;
if(ty[i]!=ty[x])
{
++idu[ty[x]];//入度加1
++odu[ty[i]];//出度加1
}
}
}
for(register int i=1;i<=typ;++i)
{
if(!idu[i])//入度为0
{
++ans1;
}
if(!odu[i])//出度为0
{
++ans2;
}
}
cout<<ans1<<'\n'<<(typ==1?0:max(ans1,ans2));//如果只有1个强连通分量,第二个数为0
return 0;
}
效率:
LibreOJ:(更改了数据范围为101,否则会MLE)
测试点 #1 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #2 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #3 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #4 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #5 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #6 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #7 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #8 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #9 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #10 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #11 Accepted 得分:100 用时:3 ms 内存:252 KiB
测试点 #12 Accepted 得分:100 用时:3 ms 内存:252 KiB
Luogu(USACO版):(未更改数据范围)
用时: 2745ms / 内存: 118276KB
测试点信息
1
AC 248ms/118048KB
2
AC 253ms/118276KB
3
AC 248ms/118192KB
4
AC 249ms/118052KB
5
AC 247ms/118152KB
6
AC 257ms/118052KB
7
AC 244ms/118056KB
8
AC 251ms/118072KB
9
AC 245ms/118036KB
10
AC 254ms/118032KB
11
AC 249ms/118032KB
Luogu(加强版):(很奇怪,竟然没有MLE,虽然118800KB=116.02MB)
用时: 2519ms / 内存: 118800KB
测试点信息
1
AC 249ms/117992KB
2
AC 261ms/118800KB
3
AC 249ms/118052KB
4
AC 249ms/118152KB
5
AC 251ms/117980KB
6
AC 252ms/118036KB
7
AC 249ms/118052KB
8
AC 251ms/118176KB
9
AC 253ms/118284KB
10
AC 255ms/118564KB