bzoj 3319 黑白树 并查集 or 树链剖分

本文介绍了一种结合树剖和并查集的数据结构算法,用于处理边颜色更新及查询问题。具体而言,在一棵带有黑白边标记的树上,算法支持两种操作:一是查询从指定节点到根节点路径上的第一条黑色边的编号;二是将指定路径上的所有边标记为黑色。通过并查集优化路径查找,并利用树剖技术实现高效更新。

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

Description

给定一棵树,边的颜色为黑或白,初始时全部为白色。维护两个操作:

1.查询u到根路径上的第一条黑色边的标号。
2.将u到v 路径上的所有边的颜色设为黑色。

Notice:这棵树的根节点为1

Input

第一行两个数n,m分别表示点数和操作数。
接下来n-? 1行,每行2个数u,v.表示一条u到v的边。
接下来m行,每行为以下格式:

1 v 表示第一个操作

2 v u 表示第二种操作

Output

对于每个询问,输出相应答案。如果不存在,输出0。

Sample Input

5 4

1 2

1 3

2 4

2 5

1 2

2 2 3

1 3

1 4

Sample Output

0

2

1

HINT

对于 100% 的数据:n,m<=10^6

分析:可以把白边的两个点并起来,这样找路径可以直接find找到。可以直接树剖

代码:

/**************************************************************
    Problem: 3319
    User: beginend
    Language: C++
    Result: Accepted
    Time:8196 ms
    Memory:78052 kb
****************************************************************/

#include <iostream>
#include <cstdio>
#include <algorithm>

const int maxn=1e6+1;
const int inf=0x3f3f3f3f;
using namespace std;

struct node{
    int x,y,next;
}adge[maxn*2];

struct ques{
    int id,x,ans;
}q[maxn];

struct kc{
    int x,y,time;
}c[maxn];

int n,m,i,x,y,cnt,num,sz,op,j;
int p[maxn],dep[maxn],fa[maxn],f[maxn],ls[maxn];

void add(int x,int y)
{
    adge[++cnt].x=x;
    adge[cnt].y=y;
    adge[cnt].next=ls[x];
    ls[x]=cnt;
}

void dfs(int x,int fat)
{
    fa[x]=fat;
    dep[x]=dep[fat]+1;
    for (int t=ls[x];t>0;t=adge[t].next)
    {
        int y=adge[t].y;
        if (fa[x]==y) continue;
        f[y]=(t+1)/2;
        dfs(y,x);
    }
}

int find(int x)
{
    int root,w,y=x;
    while (p[y]>0) y=p[y];
    root=y;
    y=x;
    while (p[y]>0)
    {
        w=p[y];
        p[y]=root;
        y=w;
    }
    return root;
}

void uni(int u,int v)
{
    int x=find(u);
    int y=find(v);
    if (x!=y) p[y]=x;
}

void change(int u,int v,int ti)
{
    x=find(u); y=find(v);
    while (x!=y)
    {
        if (dep[x]>dep[y]) swap(x,y);
        c[++num].x=fa[y];
        c[num].y=y;
        c[num].time=ti;
        uni(fa[y],y);
        x=find(x);y=find(y);
    }

}

int main()
{
    scanf("%d%d",&n,&m);
    for (i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }   
    dfs(1,0);
    sz=cnt;cnt=num=0;

    for (i=1;i<=m;i++)
    {
        scanf("%d",&op);
        if (op==1) 
        {
            scanf("%d",&x);
            q[++cnt].id=i;
            q[cnt].x=x;
        }
        else
        {           
            scanf("%d%d",&x,&y);
            change(x,y,i);
        }

    }   
    for (i=1;i<=sz;i=i+2)
    {
        if (find(adge[i].x)!=find(adge[i].y))
        {
            if (dep[adge[i].x]>dep[adge[i].y]) swap(adge[i].x,adge[i].y);
            c[++num].x=adge[i].x;
            c[num].y=adge[i].y;
            c[num].time=inf;
        }
    }
    for (i=1;i<=n;i++) p[i]=0;
    j=num;
    for (i=cnt;i>0;i--)
    {
        while (q[i].id<c[j].time) 
        {
             uni(c[j].x,c[j].y);
             j--;
        }     
        q[i].ans=f[find(q[i].x)];
    }
    for (i=1;i<=cnt;i++) printf("%d\n",q[i].ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值