Marked Ancestor [AOJ2170] [并查集]

题意:
有一个树,有些节点染色,每次有两种操作,第一,统计该节点到离它最近的染色父亲结点的的号码(Q),第二,为某一个节点染色(M),求第一种操作和。

输入:

输入由多个数据集组成。每个数据集都有以下格式:
输入的第一行包含两个整数N和Q,分别表示树T中的节点数和操作数。这些数字满足以下条件:1≤N≤100000和1≤Q≤100000。
下面的N-1行,每行包含一个整数pi(i = 2,...,N),它表示第i个节点的父节点的编号。
接下来的Q行按顺序包含操作。每个操作都格式化为“M v”或“Q v”,其中v是节点的编号。

样例:

6 3

1

1

2

3
3

Q 5

M 3

Q 5
0 0

样例输出:

4

分析:

这道题乍一看是一道在树上玩的图论/数据结构题?

但是我想半天都没想到有什么好一点的办法去做这道题。

根据作业专题:并查集,我们来思考如何靠近并查集。

显然并查集的功能是“并”,而这个题的要求显然是“拆”。

我们如果倒过来看,拆就变成并了,而很多拆的题目就是反过来处理使用并查集的。

那我们就把所有询问记录下来,并记录每个点被标记的最早时间。

那我们在查询父节点的时候,条件便是 当前点被标记的时间早于查询时间 ? 该点 :递归父节点(路径压缩)

我们是否担心路径压缩会出错?

不会,因为我们倒序后,压缩的是已经晚于查询节点的时间的,而我们的查询时间是不断向前走的。

代码:

 #include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define RG register ll
#define rep(i,a,b) for(RG i=a;i<=b;++i)
#define per(i,a,b) for(RG i=a;i>=b;--i)
#define ll long long
#define inf (1<<29)
#define maxn 100005
using namespace std;
ll n,T,x,cnt,tim,ans;
ll fa[maxn],tag[maxn],qa[maxn],qb[maxn];
inline ll read()
{
ll x=,f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} ll find(ll x)
{
return tag[x]<tim?x:fa[x]=find(fa[x]);
} int main()
{
while()
{
n=read(),T=read();
if(n==&&T==) return ;
ans=cnt=;
rep(i,,n) fa[i]=read(),tag[i]=i+T;
char s[];
rep(i,,T)
{
scanf("%s",s);x=read();
if(s[]=='M')
tag[x]=min(tag[x],i);
else
qa[++cnt]=x,qb[cnt]=i;
}
per(i,cnt,)
{
tim=qb[i];
ans+=find(qa[i]);
}
cout<<ans<<endl;
}
return ;
}
上一篇:httpclient瓶颈


下一篇:易企秀微场景2016最新完整版V10.5,小编亲测修复众多错误