[HEOI2016/TJOI2016]树

题目描述:

给出一棵树
有M个操作
C U 表示给 U节点打标记
Q U 表示询问 距离 U节点最近的标记节点(包括本身
初始时只有1节点有标记 一个点可以重复多次打标记

题目分析:

一开始傻敷敷的打了个树剖+线段树
然而题目并没有强制要求在线搞…
我们把所有操作读进来
cnt[i]表示i节点目前标记数目
我们先遍历树,搞出父子关系
然后用并查集
如果一个点 cnt[i]!=0
fa[i]=i
反之 fa[i]=f[i]//f[i]表示i节点的树上父亲
这样我们倒着处理,把加标记变成删标记
当一个点的标记删完了,那么就把他的并查集父亲改成树上父亲即可

题目链接:

Luogu 4092
BZOJ 4551

Ac 代码:

#include <iostream>
#include <cstdio>
const int maxm=100005;
int head[maxm],net[maxm*2],to[maxm*2],Cnt;
int f[maxm];
void addedge(int u,int v)
{
    Cnt++;
    to[Cnt]=v,net[Cnt]=head[u],head[u]=Cnt;
}
int cnt[maxm],fa[maxm],ans[maxm];
struct{
    int opt,id;
}ask[maxm];
int find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
int n,q;
void dfs(int now,int fax)
{
    f[now]=fax;
    for(int i=head[now];i;i=net[i])
    if(to[i]!=fax) dfs(to[i],now);
}
int main()
{
   //freopen("ts.in","r",stdin);
   scanf("%d%d",&n,&q);
   for(int i=1;i<n;i++)
   {
     int u,v;
     scanf("%d%d",&u,&v);
     addedge(u,v),addedge(v,u);
   }
   for(int i=1;i<=q;i++)
   {
     char s[10];
     scanf("%s%d",s,&ask[i].id);
     if(s[0]=='Q') ask[i].opt=1;
     else ask[i].opt=0,cnt[ask[i].id]++;
   }
   dfs(1,1);
   for(int i=1;i<=n;i++) fa[i]=cnt[i]?i:f[i];
   for(int i=q;i>=1;i--)
   {
     //printf("%d %d\n",i,ask[i].opt);
     if(ask[i].opt) ans[i]=find(ask[i].id);
     else
     {
        cnt[ask[i].id]--;
        if(!cnt[ask[i].id]) fa[ask[i].id]=find(f[ask[i].id]);
     }
   }
   for(int i=1;i<=q;i++)
   if(ask[i].opt) printf("%d\n",ans[i]);
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值