P12954 [GCJ Farewell Round #2] Railroad Maintenance【题解】
P12954 [GCJ Farewell Round #2] Railroad Maintenance
题目描述
你负责一个铁路网络的维护工作。该网络由 N\mathbf{N}N 个车站和 L\mathbf{L}L 条铁路线组成。每条铁路线双向服务于一个固定的车站列表(列车在列表的第一个和最后一个车站调头)。在一个车站可以从一条线路换乘到另一条线路,这意味着从车站 aaa 到车站 bbb 的行程是可行的,如果存在一个铁路线列表,其中第一条线路服务于车站 aaa,最后一条线路服务于车站 bbb,并且对于列表中任意两条相邻的铁路线,至少存在一个车站同时被这两条线路服务。
最简单的维护方式是每次关闭整条铁路线。然而,有些铁路线可能是关键线路。一条铁路线是关键线路,如果移除它后会导致至少一对车站之间的行程变得不可能。
给定现有的铁路线列表,计算其中有多少条是关键线路。
输入格式
输入的第一行给出测试用例的数量 T\mathbf{T}T。随后是 T\mathbf{T}T 个测试用例。每个测试用例以一行包含两个整数 N\mathbf{N}N 和 L\mathbf{L}L 开始:分别表示网络中的车站数量和铁路线数量。接着是 L\mathbf{L}L 组数据,每组包含 2 行。第 iii 组的第一行包含一个整数 Ki\mathbf{K}_iKi,表示第 iii 条铁路线服务的车站数量。第 iii 组的第二行包含 Ki\mathbf{K}_iKi 个整数 Si,1,Si,2,…,Si,Ki\mathbf{S}_{i,1}, \mathbf{S}_{i,2}, \ldots, \mathbf{S}_{i,\mathbf{K}_i}Si,1,Si,2,…,Si,Ki,表示第 iii 条铁路线服务的车站。
输出格式
对于每个测试用例,输出一行 Case #x: y
,其中 xxx 是测试用例编号(从 1 开始),yyy 是关键铁路线的数量。
输入输出样例 #1
输入 #1
4
4 3
3
1 2 3
2
1 4
3
4 1 3
4 4
2
1 2
2
3 4
2
3 2
2
4 1
4 3
2
1 2
2
3 4
2
3 2
4 3
2
1 2
2
3 4
4
4 1 2 3
输出 #1
Case #1: 1
Case #2: 0
Case #3: 3
Case #4: 1
说明/提示
样例解释
在样例 #1 中,第一条铁路线是关键线路,因为它是唯一服务于车站 222 的线路。由于关闭其他任何线路都不会导致至少一对车站之间的行程变得不可能,因此它们不是关键线路。
在样例 #2 中,没有关键线路。
样例 #3 与样例 #2 类似,但缺少最后一条铁路线。这使得剩余的所有铁路线都成为关键线路。
在样例 #4 中,最后一条铁路线是关键线路,因为没有它就无法从车站 111 到达车站 444。与样例 #1 类似,由于这条铁路线已经连接了所有车站,其他线路都不是关键线路。
数据范围
- 1≤T≤1001 \leq \mathbf{T} \leq 1001≤T≤100。
- 对所有 iii,2≤Ki≤N2 \leq \mathbf{K}_i \leq \mathbf{N}2≤Ki≤N。
- 对所有 i,ji, ji,j,1≤Si,j≤N1 \leq \mathbf{S}_{i,j} \leq \mathbf{N}1≤Si,j≤N。
- 对所有 i,j,j′i, j, j'i,j,j′ 且 j≠j′j \neq j'j=j′,Si,j≠Si,j′\mathbf{S}_{i,j} \neq \mathbf{S}_{i,j'}Si,j=Si,j′(每条铁路线中每个车站最多出现一次)。
根据上述定义,当没有铁路线被关闭时,所有车站对之间的行程都是可行的。
测试集 1(9 分,可见判定)
- 2≤N≤1002 \leq \mathbf{N} \leq 1002≤N≤100。
- 1≤L≤1001 \leq \mathbf{L} \leq 1001≤L≤100。
- K1+K2+⋯+KL≤200\mathbf{K}_1 + \mathbf{K}_2 + \cdots + \mathbf{K}_\mathbf{L} \leq 200K1+K2+⋯+KL≤200。
测试集 2(20 分,可见判定)
- 2≤N≤1052 \leq \mathbf{N} \leq 10^52≤N≤105。
- 1≤L≤1051 \leq \mathbf{L} \leq 10^51≤L≤105。
- K1+K2+⋯+KL≤2×105\mathbf{K}_1 + \mathbf{K}_2 + \cdots + \mathbf{K}_\mathbf{L} \leq 2 \times 10^5K1+K2+⋯+KL≤2×105。
翻译由 DeepSeek V3 完成
解析
前置知识:TarjanTarjanTarjan 算法求割点。不会的先去看 P3388 【模板】割点(割顶)
题意或许有点绕,化简一下就是:有 TTT 组测试数据,每组测试给出 NNN 和 LLL,表示有 NNN 个车站与 LLL 条双向铁路。值得注意的是一条铁路可能会经过多个点,所以还会给出第 iii 条铁路会经过 KiK_iKi 个点,这些点为 Si,1S_{i,1}Si,1~Si,KiS_{i,K_i}Si,Ki。要求有几条铁路整条关闭后,出现至少一对车站无法相互到达。这样铁路称为关键线路
很显然,这是一道关于无向图双连通的问题。
一看到“铁路整条关闭”“至少一对车站之间无法相互到达”,马上联想到割边。然而,由于一条路上会有多个点,而割边只有起点与终点两个点,故这不是有关割边做法。
又联想到割点,即去掉这个点及相关边,原图不联通。惊喜地发现,一个割点能连接多个其他点,很符合题中一条路连接多个点。于是,我们将铁路看作点 ppp,这条铁路上的所有点与 ppp 都有一条双向边,表明这条铁路上的每个点通过 ppp 都能与路上其他点相连。而关闭这条铁路,就是去掉 ppp 点及相关边,表明原来站点无法通过 ppp 点代表的铁路相连了。(说实话,这个思想有点像超级汇点) 此时判断图是否还联通:若还联通,则这条铁路不是关键线路;若不联通,则这条铁路是关键线路。于是得出结论:一条铁路是否为关键线路,取决于代表它的点 ppp 是否为割点
代码
(内有注释标明注意点)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e6+10; //点的数量为 n+l,故点数最大值设为2e6+10保险
int T;
int n,l;
//链式前向星存图
struct EDGE{int to,nxt;
}edge[MAXN];
int head[MAXN];
int tot;
void add(int u,int v){tot++;edge[tot].nxt=head[u];edge[tot].to=v;head[u]=tot;
}
//tarjan求割点(不会的先去学模板P3388吧)
int dfn[MAXN],low[MAXN];
int timer,ans;
int root;
int cut[MAXN];
void tarjan(int u){int son=0;dfn[u]=low[u]=++timer;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].to;if(!dfn[v]){son++;tarjan(v);low[u]=min(low[u],low[v]);if(low[v]>=dfn[u]){ if(u!=root||son>1) cut[u]=1;//发现u为割点//值得注意的是由于一个割点可能被访问多次,所以不能直接ans++,而只是标记它是割点}}else low[u]=min(low[u],dfn[v]);}
}
int main(){cin>>T;for(int t=1;t<=T;t++){//每组数据初始化不要忘 memset(dfn,0,sizeof dfn);memset(low,0,sizeof low);memset(head,0,sizeof head);memset(cut,0,sizeof cut);tot=ans=timer=0;cin>>n>>l;for(int i=1;i<=l;i++){int k;cin>>k;for(int j=1;j<=k;j++){int s;cin>>s;//为避免重复,代表铁路的点编号从n+1开始 //双向建边不要忘 add(n+i,s);add(s,n+i);}}//根据题意,原图一定是连通图//所以取第一个表示铁路的点来遍历图找哪些点是割点即可(其实理论上根节点可以随便取)root=n+1;tarjan(n+1); for(int i=n+1;i<=n+l;i++){ans+=cut[i];//统计代表铁路的点里有几个是割点}printf("Case #%d: %d\n",t,ans);}return 0;
}