这题我看到最好的做法就是不带路径压缩的并查集,简单明了。其实题目很类似于平时的并查集模板题,但是有一个不同的地方就是会不停地添加标记,相当于改变某一部分的祖先,那么如果按照平时的做法去路径压缩的话就会出现问题,因此我们只要把路径压缩那一块去掉就好了。
题目中当mark一个点时,我们就用fa[x]=x去更新某个点,使它作为一个并查集的根节点,然后如果要查询离x最近的mark点,那么我们就逐渐网上找,找到第一个fa[x]=x的点,然后返回它的值,注意,这过程中我们不能进行路径压缩。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int par[100005];
int find(int x)
{
return x==par[x]?x:find(par[x]);//带路径压缩后面就应该是par[x]=find(par[x])
}
int main()
{
int n,q,i,j;
while(cin>>n>>q&&(n+q)){
for(i=1;i<n;i++)
scanf("%d",&par[i+1]);//注意本题中par[i]并不需要初始化!
par[1]=1;getchar();char c;long long ans=0;//可能爆int
for(i=1;i<=q;i++){
c=getchar();scanf("%d",&j);getchar();
if(c=='M')par[j]=j;
else{
ans+=(long long)find(j);
}
}
cout<<ans<<endl;
}
return 0;
}
/*
━━━━━┒
┓┏┓┏┓┃μ'sic foever!!
┛┗┛┗┛┃\○/
┓┏┓┏┓┃ /
┛┗┛┗┛┃ノ)
┓┏┓┏┓┃
┛┗┛┗┛┃
┓┏┓┏┓┃
┛┗┛┗┛┃
┓┏┓┏┓┃
┛┗┛┗┛┃
┓┏┓┏┓┃
┃┃┃┃┃┃
┻┻┻┻┻┻
*/
此外还有一种做法就是不用并查集,直接递归解决问题。直接记录了每一个节点的父亲节点,每一次搜索就计算到他的染色父亲结点为止。二元组pair的第一个值记录当前点的祖先,第二个值则代表该点是否被染色。
#include <iostream>
#include<stdio.h>
#define MAX_N 100005
using namespace std;
typedef long long ll;
typedef pair<int ,bool> p;
p fa[MAX_N];
int main(){
int n,q;
fa[1].first = 1;
fa[1].second = true;
while(~scanf("%d%d",&n,&q)&&n>0){
ll ans = 0;
for(int i = 2;i <= n;i++){
scanf("%d",&fa[i].first);
fa[i].second = false;
}
getchar();
for(int i = 0;i<q;i++){
char ch;int a;
scanf("%c%d",&ch,&a);
getchar();
if(ch =='Q'){
while(fa[a].second==false){
a = fa[a].first;
}
ans += a;
}
else{
fa[a].second = true;
}
}
printf("%lld\n",ans);
}
}