BZOJ 4551树题解

好吧,洛谷的数据比较水暴力就可以过。。。。(而且跑到飞快)

不过(BZ水不过去)还是讲讲正规的做法。

其实一眼可以看出可以树剖,但是,码起来有点麻烦。

其实有一种更简单的离线做法。

我们很容易联想到并查集,利用并查集来维护各个点的最近的标记的祖先,但是加入标记后会产生分离的操作,这对并查集来说不好操作

所以我们先将所有的询问读入,将所有的标记都打上去。

从后往前处理。如果有一个点的标记变为了0,就将该点与它的父亲合并。

不知为何,在luogu上跑的比暴力要慢一点。。。。

# include<iostream>
# include<cstdio>
# include<cmath>
# include<cstring>
# include<algorithm>
using namespace std;
const int mn = 100005;
int n,m;
int c[mn],a[mn],fa[mn],ans[mn];
char opt[mn];
struct edge{int next,to;}e[mn*2];
int head[mn],edge_max;
inline void add_edge(int x,int y)
{
    e[++edge_max]=(edge){head[x],y};head[x]=edge_max;
}
int f[mn];//并查集的fa数组
int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
void dfs(int x)
{
    f[x]=c[x]?x:fa[x];
    for(int i=head[x];i;i=e[i].next)
    {
        int y=e[i].to;
        if (y!=fa[x]) fa[y]=x,dfs(y);
    }
}
int main()
{
    int x,y;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
      scanf("%d%d",&x,&y);
      add_edge(x,y);
      add_edge(y,x);
    }
    c[1]=1;
    for(int i=1;i<=m;i++)
    {
        scanf(" %c%d",&opt[i],&a[i]);
        if (opt[i]=='C') c[a[i]]++;
    }
    dfs(1);
    for(int i=m;i>=1;i--)
    {
        if (opt[i]=='C')
        {
          c[a[i]]--;
          if(!c[a[i]])
             f[a[i]]=fa[a[i]];
        }
        else ans[i]=find(a[i]);
    }
    for(int i=1;i<=m;i++)
    {
      if(ans[i])
         printf("%d\n",ans[i]);
    }
    return 0;
}

  

posted @ 2018-04-01 11:05  logeadd  阅读(113)  评论(0编辑  收藏  举报