hdu4366 Successor (dfs序+zkw线段树)

Successor

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2559    Accepted Submission(s): 613

Problem Description

Sean owns a company and he is the BOSS.The other Staff has one Superior.every staff has a loyalty and ability.Some times Sean will fire one staff.Then one of the fired man’s Subordinates will replace him whose ability is higher than him and has the highest loyalty for company.Sean want to know who will replace the fired man.

Input

In the first line a number T indicate the number of test cases. Then for each case the first line contain 2 numbers n,m (2<=n,m<=50000),indicate the company has n person include Sean ,m is the times of Sean’s query.Staffs are numbered from 1 to n-1,Sean’s number is 0.Follow n-1 lines,the i-th(1<=i<=n-1) line contains 3 integers a,b,c(0<=a<=n-1,0<=b,c<=1000000),indicate the i-th staff’s superior Serial number,i-th staff’s loyalty and ability.Every staff ‘s Serial number is bigger than his superior,Each staff has different loyalty.then follows m lines of queries.Each line only a number indicate the Serial number of whom should be fired.

Output

For every query print a number:the Serial number of whom would replace the losing job man,If there has no one to replace him,print -1.

Sample Input

1
3 2
0 100 99
1 101 100
1
2

Sample Output

2
-1

题意: 给一棵树,每个结点有两个值a和b,再有m个查询,查询问一个点的编号u,要求找出u的后代中某个结点v,v.a值比u.a大,v.b是所有后代中最大的那个点编号

思路: 先按照员工关系构出一棵树, 然后将树的dfs深度序列转化为线性序列,按这个序列确定各员工在线段树中的位置。将员工按ability从大到小排序,然后逐个插入线段树相应位置,每次插入前查询以其为根的子树区间中loyalty的最大值及其对应员工。处理完上面之后,对于询问O(1)输出。

代码:

C++交会RE,然后要手动加栈,时间200+ms

G++不需要手动加栈就过了,时间300+ms

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 50010;

struct _edge{
    int to,next;
};
_edge edge[N*2];
int ecnt,head[N];
void addedge(int u,int v)
{
    edge[ecnt].to = v;
    edge[ecnt].next = head[u];
    head[u] = ecnt++;
}
struct node{
    int id,a,b,l,r;
    friend bool operator < (const node &a, const node &b)
    {
        return a.a>b.a;
    }
};

int n,m,M;
int zkw[N*10][2];
node man[N];
int ans[N];
int dfscnt;
void dfs(int u,int fa)
{
    man[u].l = dfscnt++;
    for(int e=head[u];e!=-1;e=edge[e].next)
    {
        int &v = edge[e].to;
        if(v==fa) continue;
        dfs(v,u);
    }
    man[u].r = dfscnt++;
}

void add(int x,int a,int b)
{
    for(x+=M;x;x>>=1)
        if(zkw[x][0]<a)
            zkw[x][0]=a,zkw[x][1]=b;
}
int query(int l,int r)
{
    int a,b;
    a=b=-1;
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1 && zkw[l^1][0]>a) a=zkw[l^1][0],b=zkw[l^1][1];
        if(r&1 && zkw[r^1][0]>a) a=zkw[r^1][0],b=zkw[r^1][1];
    }
    return b;
}

void run()
{
    scanf("%d%d",&n,&m);
    memset(head,-1,sizeof(head));
    ecnt=0;
    int a,b,c;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        addedge(a,i);
        addedge(i,a);
        man[i].a=c;
        man[i].b=b;
        man[i].id=i;
    }
    dfscnt=1;
    dfs(0,-1);
//    for(int i=0;i<n;i++)
//        printf("%d %d %d\n",i,man[i].l,man[i].r);
    sort(man+1,man+n);

    for(M=1;M<=dfscnt+1;M*=2);
    memset(zkw,-1,sizeof(zkw));

    stack<int> stk;
    stk.push(1);
    ans[man[1].id]=-1;
    for(int i=2;i<n;i++)
    {
        if(man[i].a!=man[i-1].a)
        {
            while(!stk.empty())
            {
                int u = stk.top(); stk.pop();
                add(man[u].l,man[u].b,man[u].id);
                add(man[u].r,man[u].b,man[u].id);
            }
        }
        stk.push(i);
        ans[man[i].id] = query(man[i].l,man[i].r);
    }
    while(m--)
    {
        scanf("%d",&a);
        printf("%d\n",ans[a]);
    }
}

int main()
{
    freopen("case.txt","r",stdin);
    int _;
    scanf("%d",&_);
    while(_--)
        run();
    return 0;
}
时间: 2024-08-29 10:20:59

hdu4366 Successor (dfs序+zkw线段树)的相关文章

Luogu P2982 [USACO10FEB]慢下来 Slowing down | dfs序、线段树

题目链接 题目大意: 有一棵N个结点树和N头奶牛,一开始所有奶牛都在一号结点,奶牛们将按从编号1到编号N的顺序依次前往自己的目的地,求每头奶牛在去往自己目的地的途中将会经过多少已经有奶牛的结点. 题解: 可以发现,每一头奶牛到达目的地后,都只会对还未到达目的地的奶牛中,目的地在它目的地子树中的奶牛的答案产生贡献. 比如说在下面这棵树中,一头奶牛到达了图中深色结点,那么在还未到达目的地的奶牛中,只有目的地在深色结点子树中的奶牛才会由深色结点对答案产生贡献. 所以,我们可以在每头奶牛到达目的地后,将

Codeforces Round #200 (Div. 1) D. Water Tree(dfs序加线段树)

思路: dfs序其实是很水的东西.  和树链剖分一样, 都是对树链的hash. 该题做法是:每次对子树全部赋值为1,对一个点赋值为0,查询子树最小值. 该题需要注意的是:当我们对一棵子树全都赋值为1的时候, 我们要查询一下赋值前子树最小值是不是0, 如果是的话, 要让该子树父节点变成0, 否则变0的信息会丢失. 细节参见代码: #include <cstdio> #include <cstring> #include <algorithm> #include <i

URAL 1890 . Money out of Thin Air (dfs序hash + 线段树)

题目链接: URAL 1890 . Money out of Thin Air 题目描述: 给出一个公司里面上司和下级的附属关系,还有每一个人的工资,然后有两种询问: 1:employee x y z ,如果编号为x的员工如果工资小于y,就给他加薪z. 2:department x y z ,如果编号为x的员工所管辖的范围内(包括自己),所有员工的工资平均数小于y,给该范围加薪z. 问q次操作后这个公司内每个员工的工资为多少? 解题思路: 根据上司和下级的附属关系,可以先建一个有向图,然后对有向

【bzoj3685】普通van Emde Boas树 权值zkw线段树

原文地址:http://www.cnblogs.com/GXZlegend/p/6809743.html 题目描述 设计数据结构支持:1 x  若x不存在,插入x2 x  若x存在,删除x3    输出当前最小值,若不存在输出-14    输出当前最大值,若不存在输出-15 x  输出x的前驱,若不存在输出-16 x  输出x的后继,若不存在输出-17 x  若x存在,输出1,否则输出-1 输入 第一行给出n,m 表示出现数的范围和操作个数接下来m行给出操作n<=10^6,m<=2*10^6,

普及向 ZKW线段树!

啊,是否疲倦了现在的线段树 太弱,还递归! 那我们就欢乐的学习另外一种神奇的线段树吧!(雾 他叫做zkw线段树 这个数据结构灰常好写(虽然线段树本身也特别好写……) 速度快(貌似只在单点更新方面比线段树快……) 是一种自底向上非递归版本的线段树! 首先我们来看一个ppt,<统计的力量>这个是发明人的PPT(啊,ppt内的代码是错的…… 統計的力量 好吧,我们来写吧~ 首先预备条件: int M,T[maxn*2+2]; M指的是什么呢?M就指的是这颗zkw线段树最下面的那个点之前的编号是什么

ZKW线段树

对于区间问题,我们常用的方法是线段树.递归式的线段树具有通用性,但速度太慢.ZKW神犇使用非递归的线段树,常数特别小. 与大部分线段树一样,ZKW线段树采用堆式存储.也就是说,x节点的左儿子是x*2,右儿子是x*2+1,父亲是x/2. 由于采用非递归,我们要方便地找到叶子节点.ZKW线段树的方法是,从小到大枚举叶子节点数,直到线段树装得下.比如建立1000个节点,他从1开始枚举,2,4,8,16,--1024.发现1024足够大时,将[1, 1024)作为非叶节点,[1024, 2048)为叶子

回档|忠诚2|zkw线段树

线段树,大家一看就明白了吧,这题就是一模板题,我学zkw线段树用的.单点维护,区间最值. 题目是tyvj的忠诚2. #include"iostream" #include"cstdio" using namespace std; int M; int T[10000000]; int read() { char c=getchar(); int a=0; while (c<'0'||c>'9') c=getchar(); while (c>='0'

【模板】zkw线段树の基本操作

整理了一下zkw线段树的基本操作: 1.修改元素值:给定x,y,修改a[x]的值为y: 2.查询元素值:给定x,输出a[x]的值: 3.区间增减:给定l,r,x,将从a[l]到a[r](含al和ar),即区间[l,r]中的每个值都加上x: 4.区间求和:给定l,r,输出从a[l]到a[r](含al和ar),即区间[l,r]中的每个元素之和. #include<cstdio> #include<cstdlib> #include<cctype> #include<c

【题解】CODEVS 1217 借教室 zkw线段树(区间最值)

捣鼓了好长时间才终于把zkw线段树的区间最值修改给A掉了 = =zkw神犇的课件里竟然有错! (╯▽╰)下面的伪代码里被注释的是我修改的>< Func Add_x(s,t,x) for (s=s+M-1,t=t+M+1;s^t^1;s>>=1,t>>=1) { if (~s&1) T[s^1]+=x; if ( t&1) T[t^1]+=x; A=min(T[s],T[s^1]),T[s]-=A,T[s^1]-=A, T[s>>1]+=A;