Assign the task dfs序+线段树

本文介绍了一种用于解决公司员工任务分配问题的算法。在一个由N名员工构成的组织结构中,每位员工(除了最高领导)都有直属上司。当任务被分配给某位员工时,该员工会将任务继续分配给其所有下属,且一旦收到新任务,当前任务会被立即暂停。通过使用一种基于线段树的数据结构,我们能够高效地查询并更新员工当前的任务状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <cmath>
#include <vector>
using namespace std;
const int NUM=50005;
const int inf=0x3f3f3f3f;
struct point
{
    int l,r,w,f;
}tree[NUM*4];
int in[NUM],out[NUM];
vector<int> vec[NUM];
int time;
void dfs(int a) //dfs序
{
    in[a]=++time;   //在新序列中in[a]对应a点,区间【in[a],out[a]】对应以a为根的子树
    for(int i=0;i<vec[a].size();i++)  
        dfs(vec[a][i]);
    out[a]=time;
} //在dfs序完成后就可以不管之前的树了,用线段树对新序列进行维护就行了
int LL,RR,val;
void build(int k,int LL,int RR)
{
    tree[k].l=LL,tree[k].r=RR;
    tree[k].f=-1,tree[k].w=-1;
    if(tree[k].l==tree[k].r){
        return;
    }
    int bet=(tree[k].l+tree[k].r)/2;
    build(k*2,LL,bet);
    build(k*2+1,bet+1,RR);
}
void down(int k)
{
    tree[k*2].f=tree[k*2+1].f=tree[k].f;
    tree[k*2].w=tree[k*2].f;
    tree[k*2+1].w=tree[k*2+1].f;
    tree[k].f=-1;
}
void change(int k)
{
    if(LL<=tree[k].l&&RR>=tree[k].r){
        tree[k].w=val;
        tree[k].f=val;
        return;
    }
    if(tree[k].f!=-1) down(k);
    int bet=(tree[k].l+tree[k].r)/2;
    if(LL<=bet) change(k*2);
    if(RR>bet) change(k*2+1);
}
int ans;
void ask(int k,int point)
{
    if(tree[k].l==tree[k].r){
        ans=tree[k].w;
        return;
    }
    if(tree[k].f!=-1) down(k);
    int bet=(tree[k].l+tree[k].r)/2;
    if(point<=bet) ask(k*2,point);
    else ask(k*2+1,point);
}
int fp[NUM];
char ss[2];
int main()
{
    int t,cnt=1;
    scanf("%d",&t);
    while(t--){
        memset(fp,0,sizeof(fp));
        time=0;
        int n;
        scanf("%d",&n);
        build(1,1,n);
        for(int i=1;i<=n;i++)
            vec[i].clear();
        int a,b;
        for(int i=0;i<n-1;i++){
            scanf("%d %d",&a,&b);
            fp[a]++;
            vec[b].push_back(a);
        }
        int treepre;
        for(int i=1;i<=n;i++){ //查找树的根节点
            if(fp[i]==0){
                treepre=i;
                break;
            }
        }
        dfs(treepre);
        int m;
        scanf("%d",&m);
        printf("Case #%d:\n",cnt++);
        for(int i=0;i<m;i++){
            scanf("%s",ss);
            if(ss[0]=='C'){
                scanf("%d",&a);
                ask(1,in[a]);
                printf("%d\n",ans);
            }
            else{
                scanf("%d %d",&a,&b);
                LL=in[a],RR=out[a],val=b;
                change(1);
            }
        }
    }
    return 0;
}

 

### DFS的概念 DFS是指在深度优先搜索(Depth First Search, DFS)过程中访问节点的时间顺记录。通常分为三种时间戳: 1. **进入时间**:首次访问某节点时的时间标记。 2. **退出时间**:完成对该节点及其子树的遍历并返回上一层时的时间标记。 3. **路径列**:按照实际访问次形成的节点列。 这种列化方式能够帮助我们快速判断某些关系,例如祖先-后代关系、子树范围等[^2]。 --- ### DFS的实现 以下是基于图结构的DFS实现方法: #### 方法一:递归实现 通过递归函数,在每次访问新节点时记录其进入时间和退出时间,并将其加入路径列。 ```cpp #include <bits/stdc++.h> using namespace std; vector<vector<int>> adj; vector<int> enterTime, exitTime, pathOrder; int timer = 0; void dfs(int u) { enterTime[u] = ++timer; // 记录进入时间 pathOrder.push_back(u); // 将当前节点加入路径列 for (auto &v : adj[u]) { if (!enterTime[v]) { // 如果未访问过 dfs(v); } } exitTime[u] = ++timer; // 记录退出时间 } // 主程调用 int main() { int n, m; cin >> n >> m; adj.resize(n + 1), enterTime.assign(n + 1, 0), exitTime.assign(n + 1, 0); while (m--) { int a, b; cin >> a >> b; adj[a].push_back(b); } for (int i = 1; i <= n; ++i) { if (!enterTime[i]) { dfs(i); } } cout << "Path Order: "; for(auto node:pathOrder){ cout<<node<<" "; // 输出路径列 } } ``` 上述代码实现了基本的DFS过程,并生成了路径列 `pathOrder` 和对应的进入/退出时间数组 `enterTime`, `exitTime`[^3]。 --- #### 方法二:迭代实现 对于栈空间有限的情况,可以通过显式维护一个栈来模拟递归行为。 ```cpp stack<pair<int, bool>> stk; // pair<节点编号, 是否已处理子节点> stk.emplace(1, false); // 假设从节点1开始 while(!stk.empty()){ auto &[u, processed] = stk.top(); if(!processed){ enterTime[u] = ++timer; pathOrder.push_back(u); processed = true; // 标记为已处理 for(auto it=adj[u].rbegin();it!=adj[u].rend();++it){ // 反向压入子节点 if(!enterTime[*it]){ stk.emplace(*it, false); } } }else{ exitTime[u] = ++timer; stk.pop(); } } ``` 此版本避免了深递归可能导致的栈溢出问题。 --- ### DFS的应用 1. **验证合法性**:给定一颗树以及部分缺失的DFS,可通过重建完整的DFS来检验输入是否合理。 2. **区间查询优化**:利用DFS将树上的操作转化为线性表上的操作,从而加速动态规划或RMQ等问题求解速度。 3. **拓扑排辅助工具**:虽然主要讨论的是BFS-based Kahn算法用于有向无环图(DAG),但在特殊情况下也可以借助DFS得到逆后作为最终结果之一[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值