HDU - 3974 Assign the task (线段树 + dfs)

题目链接

题意:

一个公司中有一种制度:如果老板被分配到一个任务,那么他会把任务分配给他的直接员工,员工中如果是其他人的老板,那么该员工的员工也将被分配到这个任务。

这个公司中有 n 个人,给 m 此操作,T x y 代表给 x 分配的任务是 y,C x 代表查询 x 的任务。

题解:

我一开始是没有思路的,看了别人的解题思路才知道,如果对一棵树 dfs 的过程中一个点访问了两次,那么从第一次访问该点到第二次访问该点之间的所有点都是以该点为根节点的子树中的节点。那么建树完成之后 dfs 跑一遍求出该树的的 dfs 序和每一个节点的直接或间接儿子个数(看到有人用时间戳来求个数,感觉自己写复杂了)。这样就可以利用树的 dfs 序来建立一颗线段树了。然后每一次更新,例如将 u 点更新为 y,u 点的所有子孙有num[u]个,那么只需要把 u 到 num[u] 之间的所有点更新为 y 即可。

注意需要利用一个数组来保存每一个点在 dfs 序中的下标才能进行线段树的操作。具体看代码。

代码:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <bitset>
#define INF 0x3f3f3f3f
#define MAXM 50000 + 10
#define MAXN 50000 + 10

using namespace std;
typedef long long ll;

const ll mod = 1e9 + 7;

int t, n, m;
vector<int> G[MAXN];
int a[MAXN], cnt = 1;
struct TREE{
    int id;
    int color;
}tree[MAXN<<2];
int father[MAXN];
int id[MAXN];
int num[MAXN];

void dfs(int u, int pre)
{
    a[cnt] = u;
    id[u] = cnt ++;
    num[u] = G[u].size();
    for(int i = 0; i < G[u].size(); i ++){
        dfs(G[u][i], u);
    }
    num[pre] += num[u];
}

void push(int rt)
{
    if(tree[rt].color != -1){
        tree[rt<<1].color = tree[rt].color;
        tree[rt<<1|1].color = tree[rt].color;
        tree[rt].color = -1;
    }
}

void build(int L, int R, int rt)
{
    if(L == R){
        tree[rt].id = a[L];
        return ;
    }

    int mid = (L + R) / 2;
    build(L, mid, rt << 1);
    build(mid + 1, R, rt << 1 | 1);
}

void update(int l, int r, int c, int L, int R, int rt)
{
    if(l <= L && R <= r){
        tree[rt].color = c;
        return ;
    }

    push(rt);
    int mid = (L + R) / 2;
    if(l <= mid)
        update(l, r, c, L, mid, rt << 1);
    if(r > mid)
        update(l, r, c, mid + 1, R, rt << 1 | 1);
}

void query(int pos, int L, int R, int rt)
{
    if(L == R){
        printf("%d\n", tree[rt].color);
        return ;
    }

    push(rt);
    int mid = (L + R) / 2;
    if(pos <= mid)
        query(pos, L, mid, rt << 1);
    else
        query(pos, mid + 1, R, rt << 1 | 1);
}

void init()
{   
    cnt = 1;
    for(int i = 1; i <= n; i ++) G[i].clear();
    memset(a, 0, sizeof(a));
    memset(father, 0, sizeof(father));
    memset(tree, -1, sizeof(tree));
    memset(id, 0, sizeof(id));
}

int main()
{
    scanf("%d", &t);
    for(int i = 1; i <= t; i ++){

        printf("Case #%d:\n", i);

        scanf("%d", &n);

        init();

        for(int i = 1; i <= n - 1; i ++){
            int a, b; scanf("%d %d", &a, &b);
            G[b].push_back(a);
            father[a] = b;
        }

        for(int i = 1; i <= n; i ++){
            if(father[i] == 0)
                dfs(i, 0);
        }

        build(1, n, 1);

        scanf("%d", &m);

        for(int i = 0; i < m; i ++){
            char op; scanf(" %c", &op);
            if(op == 'C'){
                int x; scanf("%d", &x);
                query(id[x], 1, n, 1);
            }
            else{
                int x, y; scanf("%d %d", &x, &y);
                update(id[x], id[x] + num[x], y, 1, n, 1);
            }
        }
    }
    // system("pause");
}

/*

The WAM is F**KING interesting .

*/

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值