Problem F: Marked Ancestor
You are given a tree T that consists of N nodes. Each node is numbered from 1 to N, and node 1 is always the root node of T. Consider the following two operations on T:
M v: (Mark) Mark node v.
Q v: (Query) Print the index of the nearest marked ancestor of node v which is nearest to it. Initially, only the root node is marked. Note that a node is an ancestor of itself.
Your job is to write a program that performs a sequence of these operations on a given tree and calculates the value that each Q operation will print. To avoid too large output file, your program is requested to print the sum of the outputs of all query operations. Note that the judges confirmed that it is possible to calculate every output of query operations in a given sequence.
Input
The input consists of multiple datasets. Each dataset has the following format:
The first line of the input contains two integers N and Q, which denotes the number of nodes in the tree T and the number of operations, respectively. These numbers meet the following conditions: 1 ≤ N ≤ 100000 and 1 ≤ Q ≤ 100000.
The following N - 1 lines describe the configuration of the tree T. Each line contains a single integer pi (i = 2, ... , N), which represents the index of the parent of i-th node.
The next Q lines contain operations in order. Each operation is formatted as "M v" or "Q v", where v is the index of a node.
The last dataset is followed by a line containing two zeros. This line is not a part of any dataset and should not be processed.
Output
For each dataset, print the sum of the outputs of all query operations in one line.
Sample Input
6 3
1
1
2
3
3
Q 5
M 3
Q 5
0 0
Output for the Sample Input
4
问题F:标记祖先
给定一棵由N 个节点组成的树T。每个节点从 1 到N进行编号,并且节点 1 始终是T的根节点。考虑T上的以下两个操作:
M v:(标记)标记节点v。
Q v :(查询)打印距离它最近的节点v的最近标记祖先的索引。最初,仅标记根节点。请注意,节点是其自身的祖先。
您的工作是编写一个程序,在给定的树上执行一系列这些操作,并计算每个 Q 操作将打印的值。为了避免输出文件太大,您的程序需要打印所有查询操作的输出总和。请注意,评委们确认可以按给定顺序计算查询操作的每个输出。
输入
输入由多个数据集组成。每个数据集具有以下格式:
输入的第一行包含两个整数N和Q ,分别表示树T中的节点数 和操作数。这些数字满足以下条件:1 ≤ N ≤ 100000 且 1 ≤ Q ≤ 100000。
下面的N -1行描述了树T的配置。每行包含一个整数p i ( 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
题目大意:
题目需要构建一棵具有N个节点的树。初始状态标记了根节点。有2种操作。标记某节点和查询某节点距离最近的被标记的祖先节点的索引。注意,节点是其自身的祖先。输出所有查询到的离某节点最近的被标记的祖先节点的总和。
思路:
发现题目给的输入是2到N个父亲节点的索引。查询时也是需要查询父亲节点。我们可以选用并查集这种数据结构来处理这道题。但是,只需要写一条合并操作就可以了。
首先进行N-1次合并操作构建出一棵树。对于M操作,标记对应节点。对于Q操作,遍历它的祖先找到最近一个被标记的节点加入总和的计算。注意总和的数据范围超出了int。
以下是具体代码:
#include<iostream>
#include<stdio.h>
using namespace std;
#define MAX_N 100005
int Par[MAX_N];
int Mark[MAX_N];
int N,Q;
long long ans;
void unite(int x,int y)
{
Par[y]=x;
}
int main()
{
while(scanf("%d%d",&N,&Q)&&(N&&Q))
{
ans=0;
int num;
char c1;
Mark[1]=1;
for(int i=2;i<=N;++i)
{
Mark[i]=0;
}
for(int i=2;i<=N;++i)
{
scanf("%d",&num);
unite(num,i);
}
for(int i=0;i<Q;++i)
{
cin>>c1;
scanf("%d",&num);
if(c1=='M')
{
Mark[num]=1;
}
else
{
int tmp=0;
while(Par[num]!=num)
{
if(Mark[num])
{
tmp=num;
ans+=tmp;
break;
}
num=Par[num];
}
if(tmp==0)
ans+=1;
}
}
printf("%lld\n",ans);
}
return 0;
}