HDU - Assign the task(DFS&线段树|并查集)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3974
Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

There is a company that has N employees(numbered from 1 to N),every employee in the company has a immediate boss (except for the leader of whole company).If you are the immediate boss of someone,that person is your subordinate, and all his subordinates are your subordinates as well. If you are nobody’s boss, then you have no subordinates,the employee who has no immediate boss is the leader of whole company.So it means the N employees form a tree.

The company usually assigns some tasks to some employees to finish.When a task is assigned to someone,He/She will assigned it to all his/her subordinates.In other words,the person and all his/her subordinates received a task in the same time. Furthermore,whenever a employee received a task,he/she will stop the current task(if he/she has) and start the new one.

Write a program that will help in figuring out some employee’s current task after the company assign some tasks to some employee.

Input

The first line contains a single positive integer T( T <= 10 ), indicates the number of test cases.

For each test case:

The first line contains an integer N (N ≤ 50,000) , which is the number of the employees.

The following N - 1 lines each contain two integers u and v, which means the employee v is the immediate boss of employee u(1<=u,v<=N).

The next line contains an integer M (M ≤ 50,000).

The following M lines each contain a message which is either

“C x” which means an inquiry for the current task of employee x

or

"T x y"which means the company assign task y to employee x.

(1<=x<=N,0<=y<=10^9)

Output

For each test case, print the test case number (beginning with 1) in the first line and then for every inquiry, output the correspond answer per line.

Sample Input

1
5
4 3
3 2
1 3
5 2
5
C 3
T 2 1
C 3
T 3 2
C 3

Sample Output

Case #1:
-1
1
2

Problem solving report:

Description: 一个公司有n个职员,他们可能有自己的上司和下属,公司如果给其中一个职员分配任务,那么这个任务也会同时分配给他的下属,而且每个新任务分配时,此职员当前任务只能是新任务,现在要查询一些职员的当前任务是什么,若一直未分配任务则输出-1。
Problem solving: 首先打好DFS序,原多叉树每个子树的根结点就会对应一段区间(最小编号到最大编号),然后查询一个点,就是查其单点编号,更改一个点,就是更改一段对应区间。
还有一种方法就是直接找它的总上司,因为其任务是上司安排下来,所以其任务和上司的任务是一样的,所以每次查询其总上司的任务就行了,这个找总上司的过程类似于并查集。

Accepted Code:

//线段树
/* 
 * @Author: lzyws739307453 
 * @Language: C++ 
 */
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e4 + 5;

int cnt;
int head[MAXN], Adj;
int pre[MAXN], seg[MAXN << 2];

//每个老板的下属区间
struct Sub_Inter {
    int ls, rs;
}boos[MAXN];

//邻接表
struct Add_Adj {
    int data, next;
    Add_Adj() {}
    Add_Adj(int da, int ne) : data(da), next(ne) {}
}list_[MAXN];

//初始化
void init(int n) {
    cnt = Adj = 0;
    for (int i = 0; i <= n; i++) {
        pre[i] = i;
        head[i] = -1;
    }
}

//构建邻接表
void Add_edge(int u, int v) {
    list_[++Adj] = Add_Adj(v, head[u]);
    head[u] = Adj;
}

//寻找总上司(树的根节点)
int getf(int v) {
    if (pre[v] != v)
        pre[v] = getf(pre[v]);
    return pre[v];
}

//为每一个老板分配一个下属区间
void DFS(int u) {
    boos[u].ls = ++cnt;
    for (int i = head[u]; ~i; i = list_[i].next)
        DFS(list_[i].data);
    boos[u].rs = cnt;
}

//建树
void Create(int l, int r, int rt) {
    seg[rt] = -1;
    if (!(r - l))
        return ;
    int mid = l + (r - l >> 1);
    Create(l, mid, rt << 1);
    Create(mid + 1, r, rt << 1 | 1);
}

//更新子树
void PushDown(int rt) {
    if (~seg[rt]) {
        seg[rt << 1] = seg[rt << 1 | 1] = seg[rt];
        seg[rt] = -1;
    }
}

//更新任务
void Update(int Ql, int Qr, int v, int l, int r, int rt) {
    if (Ql <= l && Qr >= r) {
        seg[rt] = v;
        return ;
    }
    PushDown(rt);
    int mid = l + (r - l >> 1);
    if (Ql <= mid)
        Update(Ql, Qr, v, l, mid, rt << 1);
    if (Qr > mid)
        Update(Ql, Qr, v, mid + 1, r, rt << 1 | 1);
}

//查询任务
int Query(int Q, int l, int r, int rt) {
    if (!(r - l))
        return seg[rt];
    PushDown(rt);
    int mid = l + (r - l >> 1);
    if (Q <= mid)
        return Query(Q, l, mid, rt << 1);
    return Query(Q, mid + 1, r, rt << 1 | 1);
}

int main() {
    char op;
    int t, n, m;
    int x, y, kase = 0;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        init(n);
        Create(1, n, 1);
        for (int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            Add_edge(v, u);
            pre[u] = v;//记录上司,方便找领导者
        }
        printf("Case #%d:\n", ++kase);
        DFS(getf(1));
        scanf("%d", &m);
        while (m--) {
            scanf(" %c%d", &op, &x);
            if (op != 'T')
                printf("%d\n", Query(boos[x].ls, 1, n, 1));
            else {
                scanf("%d", &y);
                Update(boos[x].ls, boos[x].rs, y, 1, n, 1);
            }
        }
    }
    return 0;
}

Accepted Code:

//并查集
/* 
 * @Author: lzyws739307453 
 * @Language: C++ 
 */
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e4 + 5;

//任务名称、开始时间
struct Task {
    int task, time;
}seg[MAXN];

int pre[MAXN], cnt;

//初始化
void init(int n) {
    cnt = 0;
    for (int i = 0; i <= n; i++) {
        pre[i] = i;
        seg[i].task = seg[i].time = -1;
    }
}

int main() {
    char op;
    int t, n, m;
    int x, y, tmp, kase = 0;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        init(n);
        for (int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            pre[u] = v;//记录上司
        }
        printf("Case #%d:\n", ++kase);
        scanf("%d", &m);
        while (m--) {
            scanf(" %c%d", &op, &x);
            if (op != 'T') {
                y = seg[x].task;
                tmp = seg[x].time;
                while (pre[x] != x) {
                    x = pre[x];
                    if (seg[x].time > tmp)//如果找到更靠后的序号(存在更新的任务),那就更新任务,及其序号
                        y = seg[x].task, tmp = seg[x].time;
                }
                printf("%d\n", y);
            }
            else {
                scanf("%d", &y);
                seg[x].task = y, seg[x].time = ++cnt;
            }
        }
    }
    return 0;
}
展开阅读全文
©️2020 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值