zoukankan      html  css  js  c++  java
  • [洛谷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
    ",query(root,1,n,dfn[u]));
        }
        
        return 0;    
    }
    
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    一文带你彻底明白如何实现动态添加子节点及修改子节点属性
    一文带你彻底理解 JavaScript 原型对象
    Oracle内存占用高过时的调整策略
    Oracle Instant Client(即时客户端) 安装与配置
    windows环境完全卸载Oracle19c
    Oracle19c常用语句
    cannot mount database in EXCLUSIVE mode解决办法
    oracle存储过程通过游标输出Sql结果集
    Oracle DBlink的创建
    MySQL语法
  • 原文地址:https://www.cnblogs.com/lindalee/p/8393513.html
Copyright © 2011-2022 走看看