[权值线段树][DP]JZOJ 3338 法法塔的奖励

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的正整数。

分析

先考虑朴素的做法,就是从1..i-1中权值小于等于wi的f数组中提出最大的转移上来

那么在本题中就是在子树中权值小于等于wi的f数组中提出最大的转移上来

然后权值线段树合并瞎搞即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N=1e5+10;
struct Graph {
    int v,nx;
}g[N];
int cnt,list[N];
int f[N],root[N],w[N];
int t[40*N],L[40*N],R[40*N],tcnt;
int n;

int Merge(int x,int y) {
    if (!x&&!y) return 0;
    if (!x) return y;
    if (!y) return x;
    int T=++tcnt;
    t[T]=max(t[x],t[y]);
    L[T]=Merge(L[x],L[y]);
    R[T]=Merge(R[x],R[y]);
    return T;
}

void Insert(int &x,int l,int r,int k,int z) {
    if (!x) x=++tcnt;
    if (l==r&&r==k) {
        t[x]=max(t[x],z);
        return;
    }
    int mid=l+r>>1;
    if (k<=mid) Insert(L[x],l,mid,k,z);
    else Insert(R[x],mid+1,r,k,z);
    t[x]=max(t[L[x]],t[R[x]]);
}

int Query(int x,int l,int r,int ll,int rr) {
    if (rr<l||r<ll||r<l) return 0;
    if (!x) return 0;
    if (ll<=l&&r<=rr) return t[x];
    int mid=l+r>>1,c1=0,c2=0;
    if (ll<=mid) c1=Query(L[x],l,mid,ll,rr);
    if (mid<rr) c2=Query(R[x],mid+1,r,ll,rr);
    return max(c1,c2);
}

void DFS(int u) {
    for (int i=list[u];i;i=g[i].nx) {
        DFS(g[i].v);
        root[u]=Merge(root[u],root[g[i].v]);
    }
    f[u]=Query(root[u],1,n,1,w[u])+1;
    Insert(root[u],1,n,w[u],f[u]);
}

int main() {
    scanf("%d",&n);
    for (int i=1,f;i<=n;i++) {
        scanf("%d",&f);
        if (f>0) g[++cnt]=(Graph){i,list[f]},list[f]=cnt;
    }
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    DFS(1);
    for (int i=1;i<=n;i++) printf("%d ",f[i]);
}
View Code
在日渐沉没的世界里,我发现了你。
原文地址:https://www.cnblogs.com/mastervan/p/11166686.html