欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Aizu:2170-Marked Ancestor

程序员文章站 2022-07-07 18:55:53
...

Marked Ancestor

Time limit 8000 ms
Memory limit 131072 kB

Problem Description

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.

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


解题心得:

  1. 题意就是有一棵树,有两种操作,第一种是将某个点标记,第二种是询问距离某个点被标记的最进的祖先节点的编号,最后需要你打印出所有询问编号的总和。
  2. 据说这题每次回溯跑暴力都能跑出来,因为时间给了八秒嘛,其实我感觉这是考了一个并查集关于树的变形
    Aizu:2170-Marked Ancestor这样改变了树的深度,在询问答案的时候就可以O(1)得到答案,那怎么改变树的的形态呢。
  3. 其实可以将树的每个结点看成一颗新的树,每次标记的时候就可以将这个标记的节点看成根节点,形成一个子树,然后将它下面没有出现子树的部分全部合并起来。

#include <algorithm>
#include <stdio.h>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 1e5+100;

int father[maxn],n,m;
bool vis[maxn];
vector <int> ve[maxn];

void init() {
    for(int i=1;i<=n;i++) {
        ve[i].clear();
        father[i] = 1;
        vis[i] = false;
    }
    for(int i=2;i<=n;i++) {
        int temp;
        scanf("%d",&temp);
        ve[temp].push_back(i);
    }
    vis[1] = true;
}

void dfs(int x,int fa) {
    if(vis[x])
        return ;
    father[x] = fa;
    for(int i=0;i<ve[x].size();i++) {
        dfs(ve[x][i],fa);
    }
}

void Mark(int x) {
    vis[x] = true;
    father[x] = x;
    for(int i=0;i<ve[x].size();i++)
        dfs(ve[x][i],x);
}

void Solve() {
    long long sum = 0;
    for(int i=0;i<m;i++) {
        char temp[5];
        int x;
        scanf("%s%d",temp,&x);
        if(temp[0] == 'M')
            Mark(x);
        else
            sum += father[x];
    }
    printf("%lld\n",sum);
}

int main() {
    while(scanf("%d%d",&n,&m) && n|m) {
        init();
        Solve();
    }
    return 0;
}
相关标签: 并查集