[BZOJ4551] [TJOI2016 && HEOI2016]树

题目描述

Description

在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖先)你能帮帮他吗?

Input

输入第一行两个正整数N和Q分别表示节点个数和操作次数接下来N-1行,每行两个正整数u,v(1≤u,v≤n)表示u到v有一条有向边接下来Q行,形如“opernum”oper为“C”时表示这是一个标记操作,oper为“Q”时表示这是一个询问操作 对于每次询问操作,1 ≤ N, Q ≤ 100000。

Output

输出一个正整数,表示结果

Sample Input

5 5
1 2
1 3
2 4
2 5
Q 2
C 2
Q 2
Q 5
Q 3

Sample Output

1 
2 
2 
1

题目分析:

这道题可以离线询问用并查集搞 :每个点存储非自己,并离自己最近的标记点 。
删除标记时:如果标记删没了 就把并查集更改为:f[x]==fa[x]。
询问就直接find()就好了

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,m;
char s[2];
int f[110000],fa[110000];
int net[210000],head[110000],to[210000];
int flag[110000];
int tot;
void add(int x,int y)
{
    net[++tot]=head[x];
    to[tot]=y;
    head[x]=tot;
}
struct your
{
    int tmp;
    int x;
}a[110000];
int ans[110000];
int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
void dfs(int x,int temp,int dx)
{       
    fa[x]=dx;
    if(flag[x]) f[x]=x,dx=x;
    else f[x]=dx;
    for(int i=head[x];i;i=net[i])
        if(to[i]!=temp) dfs(to[i],x,dx);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }   
    flag[1]=1;
    fa[1]=1;
    for(int i=1;i<=m;i++)
    {   
        scanf("%s",&s[0]);
        scanf("%d",&a[i].x);
        if(s[0]=='C') 
        {
            flag[a[i].x]++;
            a[i].tmp=1;
        }
        else a[i].tmp=2;
    }
    dfs(1,0,1);
    for(int i=m;i>=1;i--)
    {
        ans[i]=-1;
        if(a[i].tmp==1)
        {
            flag[a[i].x]--;
            if(!flag[a[i].x]) f[a[i].x]=fa[a[i].x];
        }
        else ans[i]=find(a[i].x);
    }
    for(int i=1;i<=m;i++)
        if(ans[i]!=-1) printf("%d\n",ans[i]);
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注