【bzoj3319】【黑白树】【并查集】

本文详细介绍了如何利用并查集解决树形结构中的特定问题,包括路径上的颜色查询与修改。通过离线处理和逆序操作,实现了高效的查询与更新。适用于树形结构数据管理和算法优化。

摘要生成于 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

题解:

         bzoj3910的加强版。

         首先把操作离线.过程中可以处理出每条边都是在什么时候被染成黑色。

         然后倒着做.那就变成了删边.

         用并查集维护即可.

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1000010
using namespace std;
int point[N],next[N<<1],f[N],id[N],v[N],deep[N],x,y,n,cnt,m,ans[N],be[N],p[N],fa[N],sum[N],kind;
struct use{int st,en,p;}e[N<<1];
void add(int x,int y,int p){
  next[++cnt]=point[x];point[x]=cnt;
  e[cnt].en=y;e[cnt].p=p;
}
int find(int x){if (f[x]==x) return f[x];else return f[x]=find(f[x]);}
void dfs(int x){
  for (int i=point[x];i;i=next[i])
    if (e[i].en!=fa[x]){
      id[e[i].en]=e[i].p;fa[e[i].en]=x;
      deep[e[i].en]=deep[x]+1;
      dfs(e[i].en);
    }
} 
void up(int x,int y,int k){
    x=find(x);y=find(y);
    while (x!=y){
        if (deep[x]<deep[y]) swap(x,y);
        if (be[x]!=m+1) x=f[x];
        else be[x]=k,f[x]=f[fa[x]],x=f[x];
    }
}
int main(){
  scanf("%d%d",&n,&m);int now=n;
  for (int i=1;i<=n-1;i++){
    scanf("%d%d",&x,&y);
    add(x,y,i);add(y,x,i); 
  } 
  for (int i=1;i<=n;i++) f[i]=i,be[i]=m+1;
  dfs(1);memset(p,-1,sizeof(p));
  for (int i=1;i<=m;i++){
    scanf("%d",&kind);
    if (kind==2){scanf("%d%d",&x,&y);up(x,y,i);}
    else {scanf("%d",&x);p[i]=x;}
  }
  for (int i=1;i<=n;i++) sum[be[i]]++;
  for (int i=2;i<=m+1;i++) sum[i]+=sum[i-1]; 
  for (int i=1;i<=n;i++) v[sum[be[i]]--]=i;
  for (int i=1;i<=n;i++) f[i]=i;
  //for (int i=1;i<=n;i++) cout<<id[i]<<endl;
  for (int i=m+1;i>=1;i--){
    if (p[i]!=-1) p[i]=id[find(p[i])];
    else
      for (;be[x=v[now]]==i&&now;now--) f[x]=f[fa[x]];  
  }
  for (int i=1;i<=m;i++) if (p[i]!=-1) printf("%d\n",p[i]);
} 


         


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值