你是否还在写,假的记录重边

P8436 【模板】边双连通分量

ivyjiao @ 2024-08-15 17:01:58

如果你学过 Tarjan,就知道,为了保证答案正确,要在跑图的时候记录重边,如果你还学过边双连通分量(P8436),那相信你也知道边双连通分量在执行 Tarjan 求强连通分量或割边的时候也需要记录重边。

然而学过边双连通分量的同学,在 Tarjan 的时候可能会看到一段非常优美的记录重边代码:

#define PII pair<int,int>
...
map<PII,int>mp;
...
if(v==fa&&mp[{u,v}]+mp[{v,u}]==1) continue;
...
cin>>u>>v;
mp[{u,v}]++;

没错在 Tarjan 中当前边是不是重边只取决于当前边被记录了几次,原理是什么我不再做过多解释,大家自己也清楚。

然后你可能觉得既然模板题的 Tarjan 这么写可以,那么任何题的 Tarjan 这么写也可以,于是你高高兴兴地写完了这份简洁而优美的代码并在洛谷上获得一个 AC,你欣喜若狂的用这份代码疯狂在各种 Tarjan 题上获得 AC,直到有一天你在一道 SP 题 上出了锅,你死活不知道你哪错了,瞪着你的代码看了半天也发现不出来任何错误,直到 SP 上给你来了这句话。

Time Limit Exceeded.edit

这时,有 114514 个问号在你的大脑中蹦出,“我明明在循环中判断了重边,并且时间复杂度正确,为什么会 TLE???”

这是因为你的 Tarjan 中的记录重边方法导致你对于每条重边你并没有把它过滤掉,还是让它参与了运算,相当于重边可以无限次走,但这只是不影响结果罢了,这本身是一种浪费时间的行为,再有 map 这个 STL 的常数十分的大,导致你最终被卡常。

上文中的“你”指的都是我,你说我是不是个小丑?

聪明的你可能就想问了,那为什么洛谷的模板题这么写就没这个问题,因为这是 SP 的机子,模板题用的是洛谷的机子,而且洛谷的模板题本来 2s 能过的东西他给你开了 5s。

那我们该怎么改这份代码呢?

很简单,把 Tarjan 时记录的父节点改成来时边的编号即可,或者可以这么写:

unordered_map<int,bool>mp;
//注意,这里一定要在 Tarjan 函数的内部声明,如果只是在里面 clear 以下的话会 WA。
//因为递归的过程也会 clear 同一个 umap,导致你之前记录的东西荡然无存。
...
mp[v]=1;
if(v==fa&&mp[v]==1) continue;

by ivyjiao @ 2024-08-15 17:03:53

作为对比:

修改前:

29 4.47s,#30 4.92s

修改后:

29 1.48s,#30 1.44s


by MarSer020 @ 2024-08-15 17:11:55

@ivyjiao 神踏吗 tarjan 还带个 map/umap,不卡你卡谁


by wukaichen888 @ 2024-08-22 22:44:14

@ivyjiao 神踏吗 tarjan 还带个 map/umap,不卡你卡谁


|