【NOIP2016模拟7.11】树 [bzoj] 4551: [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

The solution

题解有很多方法,所以在这里只介绍并查集的方法。
将所有的操作倒序处理,利用并查集维护。

对于一个修改操作,相当于将这联通块与它父亲的联通块和并
对于一个查询操作,直接查询所在的块的父亲即可。

至于细节随便乱搞乱搞yy一下即可。

CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define N 200005 
using namespace std;
int n,Q,x,y,t[N],last[N],next[N],Dad[N],f[N],mark[N],l=0,ans[N],a[N];
bool bz[N];
char ch[3];
inline int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
void add(int x,int y)
{
    t[++l]=y;
    next[l]=last[x];
    last[x]=l;
}
int get(int x) {return x==Dad[x]?x:Dad[x]=get(Dad[x]);}
void dfs(int x)
{
    if (mark[x]) Dad[x]=x;
    else Dad[x]=f[x];
    for (int k=last[x];k;k=next[k])
    if (t[k]!=f[x]) f[t[k]]=x,dfs(t[k]);
}
int main()
{
    read(n);read(Q);
    fo(i,1,n-1) read(x),read(y),add(x,y),add(y,x);  
    fo(i,1,Q)
    {
        scanf("%s%d",&ch,&a[i]);
        if (ch[0]=='C') mark[a[i]]++,bz[i]=true;
    }
    mark[1]++;
    dfs(1);
    fd(i,Q,1)
        if (bz[i])
        {
            mark[a[i]]--;
            if (mark[a[i]]==0) Dad[a[i]]=f[a[i]];
        }
        else ans[i]=get(a[i]);
    fo(i,1,Q) if (!bz[i]) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值