<并查集>luogu 1196 银河英雄传说

来源:互联网 发布:珍嗖啦淘宝上是真的吗 编辑:程序博客网 时间:2024/05/27 00:31

去题面的传送门
第一反应是并查集维护战舰之间的集合关系,记录每一个战舰前面的战舰数量pre[i],记录每一个并查集的集合大小rank[i],每次合并更新pre的大小,查询时如果两艘战舰在不同的集合里,直接return -1。反之,返回两艘战舰的pre之差-1,就是两艘战舰之间的战舰数量
但是对于如何维护pre,我调试了很久。
一开始想的是,每次合并时,把排列在后面的那个集合的根节点的pre+前面的集合的集合大小,在路径压缩时顺便求一下子节点的pre[x]=max(pre[x],pre[fa[x]]+1)。但是这样肯定有bug啊,一旦将一个集合路径压缩完成,他原先的树结构就打破了,所以这个式子是不对的。
然后我又记录了一个son[i]表示每个节点在路径压缩之前的儿子。写一个findson函数,每次合并时,都把排在后面的集合按照son把它所在的集合的子节点遍历一遍,改变他们的pre值,这样就不会破坏原来的结构了。
这是这种做法的代码(61分):

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=30000+10;int t;int fa[maxn],rank[maxn],son[maxn],pre[maxn];int findfa(int x){    if(fa[x]==x) return x;    return fa[x]=findfa(fa[x]);}int findson(int x,int v){    pre[x]+=v;    if(son[x]==x) return x;    return findson(son[x],v);}void merge(int x,int y)//x合并到y上 {    int fx=findfa(x),fy=findfa(y);    fa[fx]=fy;    son[findson(fy,0)]=fx;    findson(fx,rank[fy]);    rank[fy]+=rank[fx];}int ask(int x,int y){    int fx=findfa(x),fy=findfa(y);    if(fx!=fy) return -1;    return max(pre[y],pre[x])-min(pre[x],pre[y])-1;}int main(){    scanf("%d",&t);    for(int i=1;i<=maxn;++i) fa[i]=i,rank[i]=1,son[i]=i;    while(t--)    {        char c;        int x,y;        cin>>c;        scanf("%d%d",&x,&y);        if(c=='M') merge(x,y);        else printf("%d\n",ask(x,y));    }    return 0;}

其实显然这样会超时。顺着找一遍son,相当于没有路径压缩的并查集,时间复杂度很高,这份代码交到洛谷上WA了3个点。
回到最初的想法。
虽然破坏了树结构,但其实我们照样能做。
合并时还是没有变,把排在后面的集合的根节点的pre+=前面集合的大小。在路径压缩时,pre[x]+=pre[fa[x]]。先递归更新pre[fa[x]]的值,再回来改变pre[x]。如果fa[x]指向的是根节点,由于根节点的pre值一定是0,所以pre[fa[x]]一定不会变化。如果fa[x]指向的不是根节点也就是说之前他还没有路径压缩,我们就继续递归下去,并改变pre。
代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=30000+10;int t;int fa[maxn],rank[maxn],pre[maxn];int find(int x){    if(fa[x]==x) return x;    int f=fa[x];    fa[x]=find(fa[x]);    pre[x]+=pre[f];    return fa[x];}void merge(int x,int y)//x合并到y上 {    int fx=find(x),fy=find(y);    fa[fx]=fy;    pre[fx]+=rank[fy];    rank[fy]+=rank[fx];}int ask(int x,int y){    int fx=find(x),fy=find(y);    if(fx!=fy) return -1;    return max(pre[y],pre[x])-min(pre[x],pre[y])-1;}int main(){    scanf("%d",&t);    for(int i=1;i<=maxn;++i) fa[i]=i,rank[i]=1;    while(t--)    {        char c;        int x,y;        cin>>c;        scanf("%d%d",&x,&y);        if(c=='M') merge(x,y);        else printf("%d\n",ask(x,y));    }    return 0;}
原创粉丝点击