Aizu - 2170 变形并查集(进阶)

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.

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

题目大意:建立一棵树,这棵树的关系在n-1行中已经给出,然后就是有m次对树的操作,其中 Q  v代表查询离v点最近被标记的点(一开始只有1号点被标记,也就是根节点被标记),M v代表标记一下v点,最终输出所有被查询到的点的和

分析:利用并查集的方法,寻找根节点,只要找到一个里需要查询的点最近的点就作为递归出口,其中,不再需要将路径压缩

ps一下,这问题int会越界,用long long好一些

#include <iostream>
#include<queue>
#include <cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define init(i,a) memset(i,a,sizeof i)
typedef long long ll;
using namespace std;
const int N = 1e5+19;
int f[N];
int m,n;
bool vis[N];
int Get(int v){
    int ans=0;
    if(vis[v]) return v;
    else{
        ans=Get(f[v]);
        return ans;
    }
}
int main(){
//    #ifndef ONLINE_JUDGE
//    freopen("in.txt","r",stdin);
//    #endif
    while(scanf("%d%d",&n,&m),m,n){
            init(vis,0);
    vis[1]=1;
    rep(i,2,n){
    scanf("%d\n",&f[i]);
    }
    char ch;int v;
    ll ans=0;
    while(m--){
    scanf("%c %d\n",&ch,&v);
        if(ch=='Q')  ans+=Get(v);
        else vis[v]=1;
    }
    printf("%lld\n",ans);
    }

    return 0;
}

另外一种方法

正解:将所有被标记的结点记录他们最早被标记的时间,然后将所有询问记录下来并倒着处理,因为如果我们倒着处理询问,那么我们处理完一个询问以后,时间上在这个询问之后的标记操作都可以忽略掉了,具体在树上怎么进行‘忽略’操作呢,这时候就要用到神奇的并查集了,并查集的路径压缩刚好可以做到这点,在处理一个询问的过程中,并查集寻根操作时会经过很多未标记点和很多‘标记过期’的点,这些点我们都可以直接忽略掉而让被询问点直接指向离他最近的并且标记尚未过期的点,这样就进行了树的重构,使得树高不断减小,从而降低了询问花费的时间。

感觉这个树重构的过程就是并查集路径压缩过程的完美体现。
----------
原文:https://blog.csdn.net/lxy767087094/article/details/75947539?utm_source=copy

#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<cmath>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn=1e5+90;
int f[maxn];
int mark[maxn];
int tt[maxn],e[maxn];
int Qt;//最新当前点的更新时间
int Get(int v){
    if(mark[v]<Qt)//当前查询的点已经更新了,那就直接返回
        return v;//返回下标
    else{
        f[v]=Get(f[v]);//并查集的压缩路径
        return f[v];
    }
}

int main()
{

    int N, Q;
    while(scanf("%d%d",&N,&Q),N){
        for(int i = 2; i <= N; i++){
            scanf("%d",&f[i]);
            mark[i] = INF;
        }
        getchar();
        int c = 0;
        char ch;int v;
        for(int i = 1; i <= Q; i++){
            scanf("%c %d\n",&ch,&v);
            if(ch=='Q'){
                tt[c]=i;//需要查询的点的时间
                e[c++]=v;
            }else{
            mark[v]=min(mark[v],i);//更新点,进而覆盖
            }
        }
        long long ans=0;
        while(c--){//这里一定是倒叙实现,因为上面的查询的部分需要更新,否则会导致结果偏小;
        Qt=tt[c];
        ans+=Get(e[c]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/83048492