线段树:并查集缩点

题目描述

给定一个n个点n-1条边的无向连通图(一棵树),并对图中的边进行m次染色操作。

每次染色操作给定2个点u、v和一种颜色c,并将图中u,v之间的最短路上的边都染成这种颜色。

询问的是最终图中每条边的颜色。(若未被染色则视为颜色0即无色)

题解

我们会发现一个点只会被最后一种颜色影响,所以我们从后往前更新,更新边的信息可以保存在下一个点上,最后并查集缩点即可

并查集缩点

我们可以把并查集的 f i n d ( ) find() find()函数理解成一个函数,传入一个值,返回一个映射,如果这个点被更新了,就要往上映射到它父亲被映射到的地方,所以 p r e [ x ] = f i n d ( f a [ x ] ) pre[x]=find(fa[x]) pre[x]=find(fa[x])

代码

#include <bits/stdc++.h>
#define maxn 500005
using namespace std;
inline int read(){
    int res; bool f=1; char c;
    while(!isdigit(c=getchar())) if(c=='-') f=0; res=(c^48);
    while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
    return f?res:-res;
}
struct EDGE{
    int u,v,nxt;
}e[maxn<<1];
int cnt,head[maxn],pre[maxn];
inline void add(int u,int v){
    e[++cnt]=(EDGE){u,v,head[u]};
    head[u]=cnt;
}
inline int find(int x){return x==pre[x]?x:pre[x]=find(pre[x]);}
int dep[maxn];
int s[maxn],t[maxn],c[maxn],fa[maxn],ans[maxn];
void DFS(int u){
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v; if(dep[v]) continue;
        fa[v]=u; dep[v]=dep[u]+1; DFS(v);
    }
}
int n,m;
int main(){
    n=read(); m=read();
    for(int i=2;i<=n;i++){
        int fa=read();
        add(fa,i); add(i,fa);
    }
    dep[1]=1; DFS(1);
    for(int i=1;i<=n;i++) pre[i]=i;
    for(int i=1;i<=m;i++){
        s[i]=read(); t[i]=read(); c[i]=read();
    }
    for(int i=m;i;i--){
        int x=find(s[i]),y=find(t[i]); if(x==y) continue;
        while(x^y){
            if(dep[x]<dep[y]) swap(x,y);
            ans[x]=c[i];
            x=pre[x]=find(fa[x]);
        }
    }
    for(int i=2;i<=n;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarden_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值