HDU 3974 Assign the task(线段树维护树的dfs序)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3974

解题思路:

将所有的关系建树,每次修改每一节点的同时对其所有子节点修改。

我们可以利用dfs序。

记录某一个点进入(开始遍历子节点)的顺序,出去(所有子节点全部遍历)的顺序

如图,其中一种dfs序为1 2 4 4 5 5 2 3 3 1,在一个节点这对位置中间都是都是这个节点的子节点。

建线段树。

in[x],out[x]表示x节点进出在dfs序中的位置,每次单点修改相当于[ in[x],out[x] ]的区间修改。

询问就so easy啦。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define mid int m=l+r>>1
#define tl tree[rt<<1]
#define tr tree[rt<<1|1]

using namespace std;

const int N = 5e4+5;

int tree[N<<3];///N*2 *4
int in[N],out[N],tot;///dfs进的位置,出的位置
struct node{///链式前向星建边
    int last,to;
}edge[N];
int head[N],id;

void add(int u,int v){edge[id].to = v; edge[id].last = head[u]; head[u] = id++;}

void dfs(int now){
    in[now] = ++tot;//printf("in[%d]=%d\n",now,in[now]);
    for (int i=head[now];i!=0;i=edge[i].last){
        int v = edge[i].to;
        dfs(v);
    }
    out[now] = ++tot;//printf("out[%d]=%d\n",now,out[now]);
}

void push_up(int rt){
    if (tl==tr) tree[rt] = tl;
    else tree[rt] = -2;///表示区间有不同颜色,初始-1也当做一种颜色
}

void push_down(int rt){
    tl = tr = tree[rt];
}

void update(int L,int R,int x,int rt,int l,int r){
    if (L<=l && r<=R){
        tree[rt] = x;
        return ;
    }
    mid;
    if (tree[rt]>=0) push_down(rt);///区间下放,-1不用,-1的话子节点肯定是-1
    if (L<=m) update(L,R,x,lson);
    if (R>m)  update(L,R,x,rson);
    push_up(rt);
}

int query(int p,int rt,int l,int r){
    if (tree[rt]>=-1){///只要不是-2,这个区间都是一种颜色。
        return tree[rt];
    }
    if (l==r){
        return tree[rt];
    }
    mid;
    if (p<=m) return query(p,lson);
    else return query(p,rson);
}

void build(int rt,int l,int r){
    if (l==r){
        tree[rt] = -1;
        return ;
    }
    mid;
    build(lson);
    build(rson);
    push_up(rt);
}

int main()
{
    int T;
    scanf("%d",&T);
    for (int sb = 1;sb<=T;sb++){
        id = 1;
        tot = 0;
        memset(head,0,sizeof head);
        memset(in,0,sizeof in);///这个数组还用于统计入度找root节点,所以初始化
        printf("Case #%d:\n",sb);

        int n;
        scanf("%d",&n);
        for (int i=1;i<n;i++){
            int u,v;
            scanf("%d %d",&u,&v);
            add(v,u);
            in[u]++;
        }
        int root;
        for (int i=1;i<=n;i++)///找root节点
            if (in[i]==0){
                root = i;
                break;
            }

        dfs(root);
        build(1,1,tot);

        int q,x,y;
        char op[2];
        scanf("%d",&q);
        while (q--){
            scanf("%s",op);
            if (op[0]=='C'){
                scanf("%d",&x);
                printf("%d\n",query(in[x],1,1,tot));
            }
            else {
                scanf("%d %d",&x,&y);
                update(in[x],out[x],y,1,1,tot);
            }
        }
    }
    return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值