【并查集】HDU6334 Problems on a Tree

博客讲述了使用并查集解决HDU6334题目中的树形问题,涉及树的边有三种权值,讨论了如何处理修改边的权值和查询路径条件的问题。分析中提到了维护两个并查集,分别对应只走1的边可达最远点和1或2的边可达最远点,并解释了如何根据这些信息解答第一问和第二问。

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

题意:

给出一颗,树上有三种权值的边。
给出m次修改以及询问

每次修改将某条边的权值-1(1则不变)

定义从uu能走到v的条件是,在从uuv的路径上,遇到第一条权值为3的边后,之后只有权值为1的边。

询问有2个,首先询问能否从t走到s,其次询问有多少点能走到s


分析:

考场上再次看错题。。。。尼玛是棵树啊!!!!

后来发现错误,想到正解已经是最后30分钟了。。。没调出来GG

其实是树的话就很简单了。

首先,可以存一个点向上,只走权值为1的边能达到的最远点(设为f1(x)f1(x))。以及只走1或2,能到达的最远点(设为f2(x)f2(x))。(维护可以用并查集)

设一个点的父亲节点为fa(x)fa(x)

第一问

这样一来,询问的第一问就很简单了,只需要考虑3种情况:
1f2(s)=f2(t)1、f2(s)=f2(t),说明只经过1或2的边就能走到。
2f2(fa(f1(s)))=f2(t)2、f2(fa(f1(s)))=f2(t),说明从s向上只走1的边,直到第一个非1的边,再只走1或2的边就能到达t(即包含了经过权值为3的边的情况)
3f1(s)=f1(fa(f2(t)))3、f1(s)=f1(fa(f2(t))),说明从t向上走权值为1或2的边,直到第一个3的边,再只走1就能到达s(这种情况主要是对s为t的祖先而处理的,相对应的,2情况能处理t是s的祖先的情况,而这个不能。如果s和t没有祖先关系,这种情况其实和2等价)

第二问

然后就是如何维护第二问的答案。

这里又要用到上面两个并查集了。
换一种说法定义这两个并查集,无非就是:只经过1的边形成的联通块(f1(x)f1(x)即为x所在并查集的根),只经过1或2的边形成的联通块(f2(x)f2(x)即为x所在并查集的根)。

考虑所有能到达s的点需要满足的条件:必须是从s出发,只经过1的边,直到一条非1的边(可为2可为3)后,只经过1或2的边能到达的点。

那么就可以把答案分为2部分,一部分是只经过1或2的边能达到的点数,一部分先经过1的边再经过一条3的边能到达的点数。

(其实说是这样说,但实现的时候却是两部分混在一起求的)

首先两个并查集分别维护一个值,第一个并查集(只有1的边)维护从它向下走,遇到第一个3的边后,只经过1或2的边能到达的点的个数(定义为siz1(f1(x))siz1(f1(x)))。
第二个并查集(有1或2的边)维护它的集合大小(定义为siz2(f2(x))siz2(f2(x)))。

这样一来,询问时,首先加上siz2(f2(x))siz2(f2(x))

然后判断其向上第一条非1的边是否为3,如果是3,则加上经过那条边后到达的点yysiz2(f2(y))(这里其实是第二部分的答案,但因为不方便维护祖先的信息,所以在查询的时候求)。

然后再加上siz1(f1(x))siz1(f1(x))即可。

连边(更新)的时候,设两个点分别为x,y(xy)x,y(x为y的父亲)
如果是2的边(从3变为2的边),
siz1(f1(x))siz1(f1(x))减去siz2(y)siz2(y),siz1(f1(fa(x)))siz1(f1(fa(x)))加上siz2(y)siz2(y),即表示原来(x,y)这条3的边没有了,要更新它能贡献的新的祖先的信息。
如果1的边(从2变为1的边),则直接并查集合并即可。


这里说一下题外话。我看了标程的实现。。发现有一堆用不着的东西。。。看来这标程的作者自己也没想清楚啊。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define y1 We_can_read_of_things_that_happened_5,000_years_ago_in_the_Near_East,_where_people_first_learned_to_write._But_there_are_some_parts_of_the_word_where_even_now_people_cannot_write._The_only_way_that_they_can_preserve_their_history_is_to_recount_it_as_sagas_--_legends_handed_down_from_one_generation_of_another._These_legends_are_useful_because_they_can_tell_us_something_about_migrations_of_people_who_lived_long_ago,_but_none_could_write_down_what_they_did._Anthropologists_wondered_where_the_remote_ancestors_of_the_Polynesian_peoples_now_living_in_the_Pacific_Islands_came_from._The_sagas_of_these_people_explain_that_some_of_them_came_from_Indonesia_about_2,000_years_ago.
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
int fa[MAXN],f1[MAXN],f2[MAXN],dep[MAXN];
int siz[MAXN],siz2[MAXN];
vector<int> a[MAXN],w[MAXN];
int get_fa1(int x){
    if(!f1[x])
        return x;
    f1[x]=get_fa1(f1[x]);
    return f1[x];
}
int get_fa2(int x){
    if(!f2[x])
        return x;
    f2[x]=get_fa2(f2[x]);
    return f2[x];   
}
void union1(int x,int y){
    if(dep[x]>dep[y])
        swap(x,y);
    x=get_fa1(x);
    y=get_fa1(y);
    f1[y]=x;
    siz[x]+=siz[y];
}
void union2(int x,int y){
    if(dep[x]>dep[y])
        swap(x,y);
    x=get_fa2(x);
    y=get_fa2(y);
    f2[y]=x;
    siz2[x]+=siz2[y];
    siz[get_fa1(fa[y])]-=siz2[y];
    int xx=get_fa1(fa[x]);
    //PF("{%d %d %d %d %d}\n",x,y,xx,siz[x],siz[y]);
    if(xx)
        siz[xx]+=siz2[y];
}
void dfs(int x,int fax){
    dep[x]=dep[fax]+1;
    fa[x]=fax;
    for(int i=0;i<a[x].size();i++){
        if(a[x][i]==fax)
            continue;
        siz[x]++;
        dfs(a[x][i],x); 
    }
    for(int i=0;i<a[x].size();i++){
        if(a[x][i]==fax)
            continue;
        if(w[x][i]==2)
            union2(x,a[x][i]);
        if(w[x][i]==1){
            union2(x,a[x][i]);
            union1(x,a[x][i]);
        }
    }
}
void update(int x,int y){
    x=get_fa1(x);
    y=get_fa1(y);
    if(get_fa1(x)!=get_fa1(y)){
        //if(dep[x]<dep[y])
        //  swap(x,y);
        if(get_fa2(x)==get_fa2(y))
            union1(x,y);
        else
            union2(x,y);
    }
}
int n,m,u,v,s,t,val;
int main(){
    int T;
    SF("%d",&T);
    while(T--){
        SF("%d%d",&n,&m);   
        for(int i=1;i<=n;i++)
            siz2[i]=1;
        for(int i=1;i<n;i++){
            SF("%d%d%d",&u,&v,&val);
            a[u].push_back(v);
            a[v].push_back(u);
            w[u].push_back(val);
            w[v].push_back(val);    
        }
        dfs(1,0);
        for(int i=1;i<=m;i++){
            SF("%d%d%d%d",&u,&v,&s,&t); 
            bool flag=0;
            update(u,v);
            if(get_fa2(s)==get_fa2(t)||get_fa1(fa[get_fa2(t)])==get_fa1(s)||get_fa2(fa[get_fa1(s)])==get_fa2(t))
                flag=1;
            //PF("{%d %d}\n",get_fa1(s),siz[get_fa1(s)]);
            int ans=siz2[get_fa2(s)]+siz[get_fa1(s)];
            if(get_fa2(s)!=get_fa2(fa[get_fa1(s)]))//val = 3
                ans+=siz2[get_fa2(fa[get_fa1(s)])];
            PF("%d %d\n",flag,ans);
        }
        for(int i=1;i<=n;i++){
            f2[i]=0;
            f1[i]=0;    
            siz[i]=0;
            a[i].clear();
            w[i].clear();
        }
    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值