BZOJ P4551 LOJ P2054 [TJOI2016][HEOI2016] 树【并查集】

题目分析:

总之这是一道很水的题,没有多么难,也就是普通的逆向思维一发就好了。

首先这道题是很显然的一道并查集可做的题(据说可以用一些高级的数据结构),我们正向考虑每个点所在的集合 F [ ] F[] F[]所代表的含义,很显然的一个想法就是每个点所在的集合的关键字都是它最近的打了标记的祖先,但是这样设定集合含义的话是有问题的,因为当我们对一个添加标记的时候,有可能会导致以这个点为根节点的所有节点的 F [ ] F[] F[]都发生改变,而显然通过遍历子树的方法来处理就不优秀了。

所以接下来我们考虑一下反向处理。

对于样例来说:第二个 Q 2 , Q 5 , Q 3 Q 2,Q 5,Q 3 Q2Q5Q3所在的树中打了标记的点是 1 1 1号点和 C 2 C 2 C2,也就当前的树已经标记好了所有需要标记的点,而对于第一个 Q 1 Q 1 Q1,其实就相当于在标记好了所有的点的树中删去 C 2 C 2 C2

对于所有的操作我们先记录并处理需要标记的,如果一个点被标记过了,就将 F [ I ] = I F[I]=I F[I]=I,没有标记的话就将 F [ I ] = F a [ I ] F[I]=Fa[I] F[I]=Fa[I],然后倒着进行操作,如果遇到 Q Q Q也就是询问,我们只需要 F i n d ( X ) Find(X) Find(X),如果是 C C C修改的话,就相当于删除当前的标记,由于一个点可能被标记多次,当它的标记全部被删去的时候,就如初始化一样,将它的 F [ ] F[] F[]指向父亲节点: F [ I ] = F a [ I ] F[I]=Fa[I] F[I]=Fa[I]

关键代码:

int main(){
	LL I,J,K;
	N=Read(),M=Read();
	......
	for(I=1;I<=M;I++){
		scanf("%s",CH[I]);D[I]=Read();
		if(CH[I][0]=='C'){
			Mark[D[I]]++;
		}
	}
	......
	for(I=M;I>=1;I--){
		if(CH[I][0]=='Q'){
			Ans[I]=Find(D[I]);
		} else {
			Mark[D[I]]--;
			if(Mark[D[I]]==0){
				F[D[I]]=Fa[D[I]];
			}
		}
	}
	for(I=1;I<=M;I++){
		if(CH[I][0]=='Q'){
			Write(Ans[I]),putchar('\n');
		}
	}
	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值