BZOJ 4551 [Tjoi2016&Heoi2016]树 ——并查集

树剖显然可以做。

然而有一种更神奇的方法,并查集+时光倒流。

每个节点指向它上面最近的标记节点,标记节点指向自己,然后删除标记,就可以用并查集查询了。

#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define ll long long
#define mp make_pair
#define maxn 200005
 
int opt[maxn],x[maxn],n,q,a,b,cnt[maxn];
int h[maxn],to[maxn],ne[maxn],en=0,f[maxn];
int fa[maxn],ans[maxn];
 
void add(int a,int b)
{to[en]=b;ne[en]=h[a];h[a]=en++;}
 
void dfs(int o,int fa)
{
    f[o]=fa;
    for (int i=h[o];i>=0;i=ne[i])
        if (to[i]!=fa) dfs(to[i],o);
}
 
int find(int x)
{
    if (fa[x]==x) return x;
    else return fa[x]=find(fa[x]);
}
 
int main()
{
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&q);
    F(i,2,n)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b); add(b,a);
    }
    dfs(1,0); cnt[1]++;
    F(i,1,q)
    {
        char s[10]; scanf("%s%d",s,&x[i]);
        switch(s[0])
        {
            case 'C':opt[i]=1;cnt[x[i]]++;break;
            case 'Q':opt[i]=2;break;
        }
    }
    F(i,1,n) if (cnt[i]) fa[i]=i; else fa[i]=f[i];
    D(i,q,1)
    {
        if (opt[i]==1)
        {
            cnt[x[i]]--;
            if (!cnt[x[i]]) fa[x[i]]=f[x[i]];
        }
        else ans[i]=find(x[i]);
    }
    F(i,1,q) if (opt[i]==2) printf("%d\n",ans[i]);
}

  

posted @ 2017-04-20 11:19  SfailSth  阅读(129)  评论(0编辑  收藏  举报