题解 【HEOI2016】tree树

题面

解析

其实这题可以考虑离线做法,用并查集解决。

因为仔细想,添加标记并不方便,

但如果用并查集记录下祖先,

再一一删除,就会方便很多。

先把每次操作记录下来,

同时记录下每个点被标记的次数(因为有多次标记,所以不能只用bool)。

然后dfs遍历,记录祖先。

再倒序处理,

当一个点的标记被删完时,就把它的并查集指向它的父亲,

并统计答案。

最后输出就行了!

具体看代码:

#include<bits/stdc++.h>
using namespace std;

inline int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return f*sum;
}

struct node{
    int to,next;
}e[1000001];
struct hh{
    int fa/*爸爸*/,tag/*标记*/;
    int col/*并查集*/;
}a[100001];
struct que{
    int id/*节点*/,opt/*操作*/;
    int ans;
}qe[100001];
int head[100001],cnt=0;
int n,q;

inline void add(int x,int y){
    a[y].fa=x;
    e[++cnt].to=head[x];
    e[cnt].next=y;
    head[x]=cnt;    
}

int find(int x){
    return a[x].col==x? x:find(a[x].col);
}

void dfs(int x,int fa){
    a[x].fa=fa;
    if(a[x].tag) a[x].col=x;
    else a[x].col=a[fa].col;
    for(int i=head[x];i;i=e[i].to){
        int k=e[i].next;
        if(k==fa) continue;
        dfs(k,x);
    }
}

int main(){
//    freopen("tree.in","r",stdin);
//    freopen("tree.out","w",stdout);
    n=read();q=read();
    a[1].tag=1;
    for(int i=1;i<n;i++){
        int x=read(),y=read();
        add(x,y);
    }
    for(int i=1;i<=q;i++){
        char opt;
        cin>>opt;
        qe[i].id=read();
        if(opt=='C'){
            qe[i].opt=0;
            a[qe[i].id].tag++;
        }
        else if(opt=='Q'){
            qe[i].opt=1;
        }
    }
    dfs(1,1);
    for(int i=q;i>=1;i--){
        if(!qe[i].opt){
            a[qe[i].id].tag--;
            if(!a[qe[i].id].tag) a[qe[i].id].col=a[a[qe[i].id].fa].col;
        }
        else{
            qe[i].ans=find(a[qe[i].id].col);
        }
    }
    for(int i=1;i<=q;i++){
        if(qe[i].opt){
            printf("%d
",qe[i].ans);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zsq259/p/10554922.html