HDU5923 东北赛B题 并查集

本文介绍了一种使用并查集解决特定问题的优化方法。通过记录每个节点到根节点的并查集关系,并在询问阶段合并这些关系,避免了重复计算,显著提高了效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

题解:
这道题我一开始就用并查集和dfs去做,发现超时了,就我那渣渣的优化水平,怎么都优化不了,所以去看了别人的博客,发现还可以这样做。。听说是用到了可持续化的思想…这玩意不太懂啊,ORZ。。。
做法就是将每个点到根节点的链路上的并查集关系存储起来,然后再在询问的时候将k个并查集关系合并起来,这样就不用每次询问一个数的时候都并查集一次,从而达到优化。
至于怎么看,请看我的代码。
开二维数组f[][]是将树上每个节点的并查集关系记录下来。第一维表示的是第几个节点存储的并查集关系,其他的基本都和并查集差不多,只是多了一个存储多个节点的并查集这层的伟大想法,ORZ。。好难想到。
参考博客:
http://blog.youkuaiyun.com/pootree/article/details/54970941
顺便发一下我超时的代码,第一份是超时代码,第二份是正确代码。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=10000+7;
struct node
{
    int u,v;
}map[MAXN];
int fa[MAXN];
int n,m;
int a[MAXN];
int find(int p)
{
    while(p!=fa[p])
    {
        fa[p]=fa[fa[p]];
        p=fa[p];
    }
    return p;
}
bool Union(int p,int q)
{
    int P=find(p);
    int Q=find(q);
    if(P==Q)
    return true;
    else
    {
        fa[Q]=P;
    }
    return false;
}
void init()
{
    for(int i=0;i<=n;i++)
    fa[i]=i;
}
void dfs(int x)
{
    if(x==1)
    {
        Union(map[x].u,map[x].v);
        return ;
    }
    if(a[x]!=x){
        Union(map[x].u,map[x].v);
//        printf("@@@%d  %d\n",map[x].u,map[x].v);
        dfs(a[x]);
    }
}
int main()
{
    int t,Case=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        a[1]=1;
        for(int i=2;i<=m;i++)
        scanf("%d",&a[i]);
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            map[i].u=x,map[i].v=y;
        }
        int q,k;
        scanf("%d",&q);
        printf("Case #%d:\n",Case++);
        while(q--)
        {
            scanf("%d",&k);
            for(int i=1;i<=k;i++)
            {
                int x;
                scanf("%d",&x);
                dfs(x);
            }
            int ans=0; 
            for(int i=1;i<=n;i++)
            if(fa[i]==i)
            ans++,fa[i]=i;
            printf("%d\n",ans);

//            for(int i=1;i<=n;i++)
//            printf("%d %d\n",i,fa[i]);
        }
    }
}
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=10000+7;
const int MAXM=500+7;
struct node
{
    int u,v;
}map[MAXN];
int f[MAXN][MAXM];
int n,m;
vector<int> a[MAXN];
int find(int x,int p)
{
    while(p!=f[x][p])
    {
        f[x][p]=f[x][f[x][p]];
        p=f[x][p];
    }
    return p;
//  return f[x][p] == p ? p : f[x][p] = find(x, f[x][p]);
}
void Union(int p,int q,int x)
{
    int P=find(x,p);
    int Q=find(x,q);
    if(P!=Q) 
    {
        if(P>Q)
        {
            int t=P;
            P=Q;
            Q=t;
        }
        f[x][Q]=f[x][P];//这里也可以写成f[x][Q]=P,居然比前面的写法快点。。。但是这里为了逻辑上说的过去即表示的是第x个点到根节点的并查集 
    }
}
void dfs(int x,int fa)
{
    for(int i=1;i<=n;i++)//这里表示的儿子节点的并查集继承父节点的并查集关系。比如1是2的父亲,那么2的儿子3的父亲也可以说是1. 
    f[x][i]=f[fa][i];
    Union(map[x].u,map[x].v,x);
    for(int i=0;i<a[x].size();i++)
    if(a[x][i]!=fa)
    dfs(a[x][i],x);
}
void init()
{
    for(int i=0;i<=n;i++)
    f[0][i]=i;
}
int main()
{
    int t,Case=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<=m;i++)
        a[i].clear();
        init();
        for(int i=2;i<=m;i++){
            int x;
            scanf("%d",&x);
            a[x].push_back(i);
        }
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            map[i].u=x,map[i].v=y;
        }
        dfs(1,0);
        int q,k;
        scanf("%d",&q);
        printf("Case #%d:\n",Case++);
        for(int i=1;i<=n;i++)
        f[m+1][i]=i;
        while(q--)
        {
            scanf("%d",&k);
            for(int i=1;i<=k;i++)//这里的操作就是k个并查集合并起来 
            {
                int x;
                scanf("%d",&x);
                for(int j=1;j<=n;j++)//将第x个点的并查集关系与m+1个点的并查集的关系联系起来,即相当于将k个并查集的关系合并到新建立的m+1点的并查集中去。 
                Union(find(x,j),j,m+1);
            }
            int ans=0; 
            for(int i=1;i<=n;i++)
            if(f[m+1][i]==i)
            ans++;
            else
            f[m+1][i]=i;
            printf("%d\n",ans);
        }
    }
}
/*
这道题我一开始就用并查集和dfs去做,发现超时了,就我那渣渣的优化水平,怎么都优化不了,所以去看了别人的博客,发现还可以这样做。。听说是用到了可持续化的思想...这玩意不太懂啊,ORZ。。。 
做法就是将每个点到根节点的链路上的并查集关系存储起来,然后再在询问的时候将k个并查集关系合并起来,这样就不用每次询问一个数的时候都并查集一次,从而达到优化。
至于怎么看,请看我的代码。
开二维数组f[][]是将树上每个节点的并查集关系记录下来。
*/ 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值