题解之前的小故事——细节决定成败细节决定成败细节决定成败
两天前为了练习并查集,在洛谷做了这道题,刚开始的时候没有思路就去看了题解,找到解题方向后就去自己写了代码,在本地通过样例后就去提交,结果只通过了四个点,就回去检查自己的代码,发现没有什么大问题,就去看了题解的代码,发现解题思想是一致的,就又交了一发,结果还是死活WA了六个点只有40分,又回去检查代码,发现真的没有什么问题啊,我想是不是测试点改了数据,可是这道题恰恰不能下载测试点的数据,就拿题解的代码去提交,结果题解的代码能过。这就难到我了,没有数据可测的,自己检查又检查不出来,一下午就在找bug过程中度过,结果还是没找出来。我发现这种滋味真的不好受,晚上去上课,心里也是痒痒的。第二天来了之后还是不想放弃,又想到是不是洛谷判题机有问题,拿到其他OJ上去提交也是只过了四个点,又重复了昨天的一波发愣后,我实在是有点失落了,叫上实验室的小伙伴看了之后,他也没有发现哪里出错了,我就有点想放弃了。下午去做别的事情没管这道题,但我心里想谁要是帮我找到这个bug,我一定要请他吃顿饭。
这就是这篇题解今天才来写的原因,为了兑现两天前的承诺(只可惜bug是我自己今天又在琢磨这道题时查看别人提交的代码时发现的),我晚上要好好犒劳自己(滑稽 )
——————————分割线
回归正题,写这篇博客就是为了提醒自己写代码时一定要细心谨慎,不该偷的懒一定不能偷!
基本思路:这个题是一个倒过来的并查集。题目的目的是求各个时间联通块的个数,从最后时刻往前做,并查集的初始状态即被破坏后的图。
主要思想是,并查集中处于一个联通块的祖先结点相等,当任意两个祖先不同的联通块联通时,联通块会减少一个(同时当破坏的被恢复时会增加一个),通过这种操作可以知道目前有多少个联通块。而本题只需要知道是否在一个并查集中就可知道是否增删联通块了,因此核心算法不是很难。
具体操作说明附在代码里
看我40分的代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=400010;
int n,m,k,cnt,num,fa[maxn],head[maxn],broken[maxn],ans[maxn];
bool bro[maxn];
struct node{
int from,to,next;
}e[maxn];
void add(int a,int b){
//链式前向星
e[++cnt].from=a;
e[cnt].to=b;
e[cnt].next=head[a];
head[a]=cnt;
}
int find(int x){
//寻找祖先
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void unite(int x,int y){
//合并两个连通块
fa[find(x)]=find(y);
}
int