[洛谷P4092][HEOI2016/TJOI2016]树

Description###

在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:
标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)
询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖先)
你能帮帮他吗?

Input###

输入第一行两个正整数N和Q分别表示节点个数和操作次数
接下来N-1行,每行两个正整数u,v(1≤u,v≤n)表示u到v有一条有向边
接下来Q行,形如“opernum”oper为“C”时表示这是一个标记操作,oper为“Q”时表示这是一个询问操作对于每次询问操作。

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

HINT###

30%的数据,1 ≤ N, Q ≤ 1000
70%的数据,1 ≤ N, Q ≤ 10000
100%的数据,1 ≤ N, Q ≤ 100000


想法##

这道题的做法其实挺多的……先说我自己的吧
先dfs一遍,记录dfs序
用线段树维护到结点最近的已标记祖先编号
每次标记操作,就更新它的子节点的最近已标记祖先编号(用lazy)
查询某个点就从根节点往下走,不断pushdown,更新下面结点的最近已标记祖先,一直走到要查询的那个点。
复杂度O(QlogN)

还有一种不错的做法。
把所有操作读入,要标记的节点先都标记上
dfs一遍。记录每个节点的它最近的已标记祖先(包括自己),并查集并在一起
从后往前处理每个操作。
若是标记操作,便把这个标记去掉,将它与父节点并在一起,表明它与它父节点的最近已标记祖先是同一个点。
若是查询操作,这个点的最近已标记祖先便为它所在并查集的根。
这个做法应该比线段树快,但不太好想到。


代码##

(线段树版)

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 100005;

struct node{
    int v;
    node *next;       
}pool[N],*h[N];
int cnt;

void addedge(int u,int v){
    node *p=&pool[++cnt];
    p->v=v;p->next=h[u];h[u]=p;     
}

int dep[N],dfn[N],out[N];
int tot;
void dfs(int u){
    int v;
    dfn[u]=++tot;
    for(node *p=h[u];p;p=p->next)
        if(!dep[v=p->v]){
            dep[v]=dep[u]+1;
            dfs(v);            
        }
    out[u]=tot;
}

struct tree{
    tree *ch[2];
    int v;       
}pool2[N*20],*root;
int cnt2;

void pushdown(tree *p){
    if(dep[p->v]>dep[p->ch[0]->v]) p->ch[0]->v=p->v;
    if(dep[p->v]>dep[p->ch[1]->v]) p->ch[1]->v=p->v;     
}
void build(tree *p,int l,int r){
    p->v=1;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(p->ch[0]=&pool2[++cnt2],l,mid);
    build(p->ch[1]=&pool2[++cnt2],mid+1,r);     
}
void change(tree *p,int l,int r,int L,int R,int c){
    if(dep[c]<dep[p->v]) return;
    if(l==L && r==R){
        if(dep[c]>dep[p->v]) p->v=c;
        return;
    }
    pushdown(p);
    int mid=(l+r)>>1;
    if(mid>=R) change(p->ch[0],l,mid,L,R,c);
    else if(mid<L) change(p->ch[1],mid+1,r,L,R,c);
    else {
        change(p->ch[0],l,mid,L,mid,c);
        change(p->ch[1],mid+1,r,mid+1,R,c);     
    }
}
int query(tree *p,int l,int r,int L){
    if(l==L && r==L) return p->v;
    pushdown(p);
    int mid=(l+r)>>1;
    if(mid>=L) return query(p->ch[0],l,mid,L);
    return query(p->ch[1],mid+1,r,L);  
}

int n,q;

int main()
{
    char ch;
    int u,v;
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++)
        scanf("%d%d",&u,&v),addedge(u,v);
    dep[1]=1;
    dfs(1);
    root=&pool2[++cnt2];
    build(root,1,n);
    
    for(int i=0;i<q;i++){
        ch=getchar();
        while(ch!='C' && ch!='Q') ch=getchar();
        scanf("%d",&u);
        if(ch=='C')
            change(root,1,n,dfn[u],out[u],u);    
        else
            printf("%d\n",query(root,1,n,dfn[u]));
    }
    
    return 0;    
}
posted @ 2018-01-31 17:27  秋千旁的蜂蝶~  阅读(222)  评论(0编辑  收藏  举报