JZOJ3338. 【NOI2013模拟】法法塔的奖励

Description

法法塔是很喜欢写程序的。所以冒着就算码农屌丝一辈子的风险也大无畏地写着程序。
码农们为了表彰法法塔的坚持与执着,决定给法法塔颁布奖励,为了体现码农的屌丝气质,奖励也将由法法塔自己做出选择!
所有的奖励被组织成了一棵树的形态,每个点都有一个权值。法法塔首先选择一个子树,然后选择从该子树内的一个叶子节点到该子树的根的一条路径,将路径上节点的权值依次排成一个序列,求得这个序列的最长不下降子序列长度,这个长度就是他能得到的奖品数目。要求该子树的根必须被选择。
接下来就是法法塔进行选择的时间了。尽可能多地得到奖品是自然的。那么法法塔到底能够得到多少奖品呢?这是个很纠结的问题,他决定交给你来解决。对于每个子树,他都希望你能求出他首先选择了这个子树后,他能得到的最多的奖品数目。

Input

第一行一个数n,描述树上节点的个数。
接下来一行n个数,描述树上每个点的父亲,点1为根,其父亲设为-1,其余每个点的父亲的标号都小于自己。
接下来一行n个数,表示树上每个点的权值。

Output

一行n个数,第i个数表示法法塔选择了以第i个节点为根的子树后,他可以获得的最多的奖品个数。

Sample Input

输入1:
2
-1 1
1 1

输入2:
6
-1 1 2 1 2 4
4 3 4 3 6 2

Sample Output

输出1:
2 1

输出2:
3 1 1 2 1 1

Data Constraint

n<=100000,每个数的权值都是1到n的正整数。

题解

先考虑序列上面,求最长不下降子序列的方法,
其中就有一种方法就是用线段树。
于是就将这种方法扩展到树上,而且子树根节点是必须选到的。
因为每个点的权值都在n以内,
所以线段树上面的每一个叶子节点就表示当最后一个位置的值为x的时候最长可以有多少。
那么新加入一个就是在原有线段树上面,查询小于等于它的全部的最长一个,
然后当前这个,也就是根节点,必须选的。
如果每个节点只有只有一个儿子,那么就很方便了。
如果有多个儿子,也就是线段树合并。

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 100003
#define M 103
#define db double
#define P putchar
#define G getchar
#define inf 998244353
#define pi 3.1415926535897932384626433832795
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

struct node
{
    int l,r,mx;
}tr[N*40];

int n,m,nxt[N],to[N],lst[N],x,y,tot;
int opl,opr,op,v[N],ans[N];

void ins(int x,int y)
{
    nxt[++tot]=lst[x];
    to[tot]=y;
    lst[x]=tot;
}

void mix(int x,int y,int l,int r)
{
    if(l==r)
    {
        tr[x].mx=max(tr[x].mx,tr[y].mx);
        return;
    }
    int m=(l+r)>>1;
    if(tr[y].l)
        if(!tr[x].l)tr[x].l=tr[y].l;else mix(tr[x].l,tr[y].l,l,m);
    if(tr[y].r)
        if(!tr[x].r)tr[x].r=tr[y].r;else mix(tr[x].r,tr[y].r,m+1,r);
    tr[x].mx=max(tr[tr[x].l].mx,tr[tr[x].r].mx);
}

void find(int x,int l,int r)
{
    if(!x)return;
    if(opl<=l && r<=opr)
    {
        op=max(op,tr[x].mx);
        return;
    }
    int m=(l+r)>>1;
    if(opl<=m)find(tr[x].l,l,m);
    if(m<opr)find(tr[x].r,m+1,r);
}

void work(int x,int l,int r)
{
    if(opl<=l && r<=opr)
    {
        tr[x].mx=max(tr[x].mx,op);
        return;
    }
    int mid=(l+r)>>1;
    if(opl<=mid)
    {
        if(!tr[x].l)tr[x].l=++m;
        work(tr[x].l,l,mid);
    }
    if(mid<opr)
    {
        if(!tr[x].r)tr[x].r=++m;
        work(tr[x].r,mid+1,r);
    }
    tr[x].mx=max(tr[tr[x].l].mx,tr[tr[x].r].mx);
}

void dfs(int x)
{
    for(int i=lst[x];i;i=nxt[i])
    {
        dfs(to[i]);
        mix(x,to[i],1,n);
    }
    op=0;opl=1;opr=v[x];
    find(x,1,n);
    opl=opr=v[x];
    ans[x]=op=op+1;
    work(x,1,n);
}
int main()
{
    read(n);read(x);m=n;
    for(int i=2;i<=n;i++)
        read(x),ins(x,i);
    for(int i=1;i<=n;i++)
        read(v[i]);
    dfs(1);
    for(int i=1;i<=n;i++)
        write(ans[i]),P(' ');
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lijf2001/article/details/81006997